1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <openssl/evp.h>
10 #include "ask-password-api.h"
11 #include "blockdev-util.h"
14 #include "conf-files.h"
20 #include "format-table.h"
21 #include "format-util.h"
24 #include "hash-funcs.h"
25 #include "hexdecoct.h"
26 #include "initrd-util.h"
27 #include "main-func.h"
28 #include "mkdir-label.h"
29 #include "openssl-util.h"
30 #include "ordered-set.h"
31 #include "parse-argument.h"
32 #include "parse-util.h"
33 #include "path-util.h"
34 #include "pcrextend-util.h"
35 #include "pcrlock-firmware.h"
37 #include "pretty-print.h"
38 #include "proc-cmdline.h"
39 #include "random-util.h"
40 #include "recovery-key.h"
41 #include "sort-util.h"
42 #include "terminal-util.h"
43 #include "tpm2-util.h"
44 #include "unaligned.h"
45 #include "unit-name.h"
49 static PagerFlags arg_pager_flags
= 0;
50 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
|JSON_FORMAT_NEWLINE
;
51 static char **arg_components
= NULL
;
52 static uint32_t arg_pcr_mask
= 0;
53 static char *arg_pcrlock_path
= NULL
;
54 static bool arg_pcrlock_auto
= true;
55 static bool arg_raw_description
= false;
56 static char *arg_location_start
= NULL
;
57 static char *arg_location_end
= NULL
;
58 static TPM2_HANDLE arg_nv_index
= 0;
59 static bool arg_recovery_pin
= false;
60 static char *arg_policy_path
= NULL
;
61 static bool arg_force
= false;
63 STATIC_DESTRUCTOR_REGISTER(arg_components
, strv_freep
);
64 STATIC_DESTRUCTOR_REGISTER(arg_pcrlock_path
, freep
);
65 STATIC_DESTRUCTOR_REGISTER(arg_location_start
, freep
);
66 STATIC_DESTRUCTOR_REGISTER(arg_location_end
, freep
);
67 STATIC_DESTRUCTOR_REGISTER(arg_policy_path
, freep
);
69 #define PCRLOCK_SECUREBOOT_POLICY_PATH "/var/lib/pcrlock.d/240-secureboot-policy.pcrlock.d/generated.pcrlock"
70 #define PCRLOCK_FIRMWARE_CODE_EARLY_PATH "/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock"
71 #define PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH "/var/lib/pcrlock.d/250-firmware-config-early.pcrlock.d/generated.pcrlock"
72 #define PCRLOCK_FIRMWARE_CODE_LATE_PATH "/var/lib/pcrlock.d/550-firmware-code-late.pcrlock.d/generated.pcrlock"
73 #define PCRLOCK_FIRMWARE_CONFIG_LATE_PATH "/var/lib/pcrlock.d/550-firmware-config-late.pcrlock.d/generated.pcrlock"
74 #define PCRLOCK_GPT_PATH "/var/lib/pcrlock.d/600-gpt.pcrlock.d/generated.pcrlock"
75 #define PCRLOCK_SECUREBOOT_AUTHORITY_PATH "/var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock"
76 #define PCRLOCK_KERNEL_CMDLINE_PATH "/var/lib/pcrlock.d/710-kernel-cmdline.pcrlock.d/generated.pcrlock"
77 #define PCRLOCK_KERNEL_INITRD_PATH "/var/lib/pcrlock.d/720-kernel-initrd.pcrlock.d/generated.pcrlock"
78 #define PCRLOCK_MACHINE_ID_PATH "/var/lib/pcrlock.d/820-machine-id.pcrlock"
79 #define PCRLOCK_ROOT_FILE_SYSTEM_PATH "/var/lib/pcrlock.d/830-root-file-system.pcrlock"
80 #define PCRLOCK_FILE_SYSTEM_PATH_PREFIX "/var/lib/pcrlock.d/840-file-system-"
82 /* The default set of PCRs to lock to */
83 #define DEFAULT_PCR_MASK \
84 ((UINT32_C(1) << TPM2_PCR_PLATFORM_CODE) | \
85 (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG) | \
86 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE) | \
87 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG) | \
88 (UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE) | \
89 (UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG) | \
90 (UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY) | \
91 (UINT32_C(1) << TPM2_PCR_KERNEL_BOOT) | \
92 (UINT32_C(1) << TPM2_PCR_KERNEL_CONFIG) | \
93 (UINT32_C(1) << TPM2_PCR_SYSEXTS) | \
94 (UINT32_C(1) << TPM2_PCR_SHIM_POLICY) | \
95 (UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))
97 typedef struct EventLogRecordBank EventLogRecordBank
;
98 typedef struct EventLogRecord EventLogRecord
;
99 typedef struct EventLogRegisterBank EventLogRegisterBank
;
100 typedef struct EventLogRegister EventLogRegister
;
101 typedef struct EventLogComponentVariant EventLogComponentVariant
;
102 typedef struct EventLogComponent EventLogComponent
;
103 typedef struct EventLog EventLog
;
105 struct EventLogRecordBank
{
108 LIST_FIELDS(EventLogRecordBank
, banks
);
111 typedef enum EventPayloadValid
{
112 EVENT_PAYLOAD_VALID_YES
,
113 EVENT_PAYLOAD_VALID_NO
,
114 EVENT_PAYLOAD_VALID_DONT_KNOW
,
115 _EVENT_PAYLOAD_VALID_MAX
,
116 _EVENT_PAYLOAD_VALID_INVALID
= -EINVAL
,
119 struct EventLogRecord
{
126 /* Data for firmware events (i.e. "TCG PC Client Platform Firmware Profile Specification" events) */
127 uint32_t firmware_event_type
;
128 void *firmware_payload
;
129 size_t firmware_payload_size
;
131 /* Data for userspace events (i.e. those generated by systemd in userspace */
132 Tpm2UserspaceEventType userspace_event_type
;
133 JsonVariant
*userspace_content
;
135 /* Validation result for the event payload itself, if the record contains enough information to validate the hash */
136 EventPayloadValid event_payload_valid
;
138 /* If this record matches an variant of one of our defined components */
139 EventLogComponentVariant
**mapped
;
142 /* If this record is part of an EventLogComponentVariant */
143 EventLogComponentVariant
*owning_component_variant
;
145 LIST_HEAD(EventLogRecordBank
, banks
);
148 #define EVENT_LOG_RECORD_IS_FIRMWARE(record) ((record)->firmware_event_type != UINT32_MAX)
149 #define EVENT_LOG_RECORD_IS_USERSPACE(record) ((record)->userspace_event_type >= 0)
151 struct EventLogRegisterBank
{
152 TPM2B_DIGEST observed
;
153 TPM2B_DIGEST calculated
;
156 struct EventLogRegister
{
158 unsigned n_measurements
;
159 bool fully_recognized
; /* true if all measurements in this register have been recognized to match components */
160 EventLogRegisterBank
*banks
;
163 struct EventLogComponentVariant
{
164 EventLogComponent
*component
;
169 EventLogRecord
**records
;
173 struct EventLogComponent
{
176 EventLogComponentVariant
**variants
;
181 EventLogRecord
**records
;
184 uint16_t *algorithms
;
186 bool algorithms_locked
; /* if algorithms where set explicitly by user, and we should not determine them automatically */
190 /* The hash algorithm which we focus on for matching up components */
191 uint16_t primary_algorithm
;
193 uint8_t startup_locality
;
194 bool startup_locality_found
;
196 EventLogRegister registers
[TPM2_PCRS_MAX
];
198 EventLogComponent
**components
;
201 /* Number of components which we couldn't find in the event log */
202 size_t n_missing_components
;
204 /* PCRs mask indicating all PCRs touched by unrecognized components */
205 uint32_t missing_component_pcrs
;
208 static EventLogRecordBank
*event_log_record_bank_free(EventLogRecordBank
*bank
) {
212 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogRecordBank
*, event_log_record_bank_free
);
214 static EventLogRecord
*event_log_record_free(EventLogRecord
*record
) {
215 EventLogRecordBank
*bank
;
220 free(record
->description
);
221 free(record
->firmware_payload
);
222 json_variant_unref(record
->userspace_content
);
224 while ((bank
= LIST_POP(banks
, record
->banks
)))
225 event_log_record_bank_free(bank
);
227 free(record
->mapped
);
229 return mfree(record
);
232 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogRecord
*, event_log_record_free
);
234 static void event_log_register_done(EventLog
*el
, EventLogRegister
*reg
) {
241 static EventLogComponentVariant
* event_log_component_variant_free(EventLogComponentVariant
*variant
) {
248 FOREACH_ARRAY(record
, variant
->records
, variant
->n_records
)
249 event_log_record_free(*record
);
251 free(variant
->records
);
253 return mfree(variant
);
256 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogComponentVariant
*, event_log_component_variant_free
);
258 static EventLogComponent
* event_log_component_free(EventLogComponent
*component
) {
262 FOREACH_ARRAY(variant
, component
->variants
, component
->n_variants
)
263 event_log_component_variant_free(*variant
);
264 free(component
->variants
);
268 return mfree(component
);
271 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogComponent
*, event_log_component_free
);
273 static EventLog
* event_log_free(EventLog
*el
) {
277 FOREACH_ARRAY(p
, el
->registers
, TPM2_PCRS_MAX
)
278 event_log_register_done(el
, p
);
280 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
)
281 event_log_record_free(*rr
);
284 FOREACH_ARRAY(c
, el
->components
, el
->n_components
)
285 event_log_component_free(*c
);
286 free(el
->components
);
288 free(el
->algorithms
);
294 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLog
*, event_log_free
);
296 static EventLogRecord
* event_log_record_new(EventLog
*el
) {
297 EventLogRecord
*record
;
299 record
= new(EventLogRecord
, 1);
303 *record
= (EventLogRecord
) {
305 .firmware_event_type
= UINT32_MAX
,
306 .userspace_event_type
= _TPM2_USERSPACE_EVENT_TYPE_INVALID
,
307 .event_payload_valid
= _EVENT_PAYLOAD_VALID_INVALID
,
313 static int event_log_add_record(
315 EventLogRecord
**ret
) {
317 _cleanup_(event_log_record_freep
) EventLogRecord
*record
= NULL
;
321 if (!GREEDY_REALLOC(el
->records
, el
->n_records
+1))
324 record
= event_log_record_new(el
);
328 el
->records
[el
->n_records
++] = record
;
338 static int event_log_add_algorithm(EventLog
*el
, uint16_t alg
) {
341 if (el
->algorithms_locked
) /* algorithms configured via env var, don't add any further automatically */
344 if (typesafe_bsearch(&alg
, el
->algorithms
, el
->n_algorithms
, cmp_uint16
))
347 if (!GREEDY_REALLOC(el
->algorithms
, el
->n_algorithms
+1))
350 el
->algorithms
[el
->n_algorithms
++] = alg
;
352 typesafe_qsort(el
->algorithms
, el
->n_algorithms
, cmp_uint16
);
357 static int event_log_add_algorithms_from_environment(EventLog
*el
) {
363 e
= secure_getenv("SYSTEMD_TPM2_HASH_ALGORITHMS");
368 _cleanup_free_
char *word
= NULL
;
370 r
= extract_first_word(&e
, &word
, ":", 0);
376 r
= tpm2_hash_alg_from_string(word
);
378 return log_error_errno(r
, "Unknown hash algorithm '%s'.", word
);
380 r
= event_log_add_algorithm(el
, r
);
382 return log_error_errno(r
, "Failed to add hash algorithm '%s'.", word
);
385 if (el
->n_algorithms
> 0)
386 el
->algorithms_locked
= true;
391 static EventLogRecordBank
*event_log_record_find_bank(
392 const EventLogRecord
*record
,
397 LIST_FOREACH(banks
, i
, record
->banks
)
398 if (i
->algorithm
== alg
)
404 static int event_log_record_add_bank(
405 EventLogRecord
*record
,
409 EventLogRecordBank
**ret
) {
411 _cleanup_(event_log_record_bank_freep
) EventLogRecordBank
*bank
= NULL
;
412 _cleanup_free_
void *h
= NULL
;
415 assert(hash
|| hash_size
== 0);
417 if (event_log_record_find_bank(record
, algorithm
))
420 if (hash_size
> sizeof_field(TPM2B_DIGEST
, buffer
))
423 h
= memdup(hash
, hash_size
);
427 bank
= new(EventLogRecordBank
, 1);
431 *bank
= (EventLogRecordBank
) {
432 .algorithm
= algorithm
,
433 .hash
= TPM2B_DIGEST_MAKE(hash
, hash_size
),
436 LIST_PREPEND(banks
, record
->banks
, bank
);
446 static bool event_log_record_is_stub(EventLogRecord
*rec
) {
449 /* Recognizes the special EV_IPL events systemd-stub generates. Since EV_IPL can be used by almost
450 * anything, we'll check for the PCR values, to see if it's one of ours. */
452 if (rec
->firmware_event_type
!= EV_IPL
)
455 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
458 if (!IN_SET(rec
->pcr
,
459 TPM2_PCR_KERNEL_BOOT
, /* 11 */
460 TPM2_PCR_KERNEL_CONFIG
, /* 12 */
461 TPM2_PCR_SYSEXTS
)) /* 13 */
467 static int event_log_record_parse_variable_data(
469 sd_id128_t
*ret_variable_uuid
,
470 char **ret_variable_name
) {
472 _cleanup_free_ char16_t
*p16
= NULL
;
473 _cleanup_free_
char *p
= NULL
;
476 assert(ret_variable_uuid
);
477 assert(ret_variable_name
);
479 if (rec
->firmware_payload_size
< sizeof(UEFI_VARIABLE_DATA
))
480 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI variable field too short.");
482 const UEFI_VARIABLE_DATA
*vdata
= rec
->firmware_payload
;
484 if (vdata
->unicodeNameLength
> (SIZE_MAX
- offsetof(UEFI_VARIABLE_DATA
, unicodeNameLength
)) / 2)
485 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unicode name length too large.");
487 size_t m
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + vdata
->unicodeNameLength
* 2;
489 if (vdata
->variableDataLength
> SIZE_MAX
- m
)
490 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Oversize EFI variable data size.");
492 if (rec
->firmware_payload_size
!= m
+ vdata
->variableDataLength
)
493 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI variable data has wrong size.");
495 p16
= memdup(vdata
->unicodeName
, vdata
->unicodeNameLength
* 2); /* Copy out, to align properly */
497 return log_oom_debug();
499 p
= utf16_to_utf8(p16
, vdata
->unicodeNameLength
* 2);
501 return log_oom_debug();
503 if (!string_is_safe(p
))
504 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI variable string in record.");
506 *ret_variable_uuid
= efi_guid_to_id128(vdata
->variableName
);
507 *ret_variable_name
= TAKE_PTR(p
);
512 static int event_log_record_extract_firmware_description(EventLogRecord
*rec
) {
513 _cleanup_free_
char *fallback
= NULL
;
518 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
521 if (arg_raw_description
)
524 switch (rec
->firmware_event_type
) {
526 case EV_EFI_VARIABLE_DRIVER_CONFIG
:
527 case EV_EFI_VARIABLE_BOOT
:
528 case EV_EFI_VARIABLE_BOOT2
:
529 case EV_EFI_VARIABLE_AUTHORITY
: {
530 _cleanup_free_
char *p
= NULL
;
533 r
= event_log_record_parse_variable_data(rec
, &uuid
, &p
);
537 log_warning_errno(r
, "EFI variable data invalid, ignoring.");
541 if (asprintf(&rec
->description
, "%s: %s-" SD_ID128_UUID_FORMAT_STR
,
542 rec
->firmware_event_type
== EV_EFI_VARIABLE_AUTHORITY
? "Authority" : "Variable",
544 SD_ID128_FORMAT_VAL(uuid
)) < 0)
551 if (rec
->firmware_payload_size
!= sizeof(uint32_t)) {
552 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI separator field has wrong size, ignoring.");
556 uint32_t val
= unaligned_read_ne32(rec
->firmware_payload
);
561 case UINT32_C(0xffffffff):
562 (void) asprintf(&rec
->description
, "Separator: Success (0x%02" PRIx32
")", val
);
566 rec
->description
= strdup("Separator: Error (0x01)");
570 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unexpected separator payload %" PRIu32
".", val
);
574 if (!rec
->description
)
580 case EV_EFI_ACTION
: {
581 _cleanup_free_
char *d
= NULL
;
583 r
= make_cstring(rec
->firmware_payload
, rec
->firmware_payload_size
, MAKE_CSTRING_ALLOW_TRAILING_NUL
, &d
);
585 return log_error_errno(r
, "Failed to make C string from EFI action string: %m");
587 if (!string_is_safe(d
)) {
588 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI action string in record, ignoring.");
592 rec
->description
= strjoin("Action: ", d
);
593 if (!rec
->description
)
598 case EV_EFI_GPT_EVENT
: {
599 if (rec
->firmware_payload_size
< sizeof(GptHeader
)) {
600 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "GPT measurement too short, ignoring.");
604 const GptHeader
*h
= rec
->firmware_payload
;
606 if (!gpt_header_has_signature(h
)) {
607 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "GPT measurement does not cover a GPT partition table header, ignoring.");
611 if (asprintf(&rec
->description
, "GPT: disk " SD_ID128_UUID_FORMAT_STR
, SD_ID128_FORMAT_VAL(efi_guid_to_id128(h
->disk_guid
))) < 0)
618 _cleanup_free_
char *d
= NULL
;
620 /* EV_IPL can be anything, only try to parse the description on PCRs we "own" */
621 if (!event_log_record_is_stub(rec
))
624 /* sd-stub always sets a description string as text for these */
626 d
= utf16_to_utf8(rec
->firmware_payload
, rec
->firmware_payload_size
);
630 if (string_has_cc(d
, NULL
)) {
631 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI action string in record, ignoring.");
635 rec
->description
= strjoin("String: ", d
);
636 if (!rec
->description
)
643 TCG_PCClientTaggedEvent
*tag
= rec
->firmware_payload
;
644 size_t left
= rec
->firmware_payload_size
;
647 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Empty tagged PC client event, ignoring.");
654 if (left
< offsetof(TCG_PCClientTaggedEvent
, taggedEventData
)) {
655 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Tagged PC client event too short, ignoring.");
659 m
= offsetof(TCG_PCClientTaggedEvent
, taggedEventData
) + (uint64_t) tag
->taggedEventDataSize
;
661 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Tagged PC client event data too short, ignoring.");
665 switch (tag
->taggedEventID
) {
667 /* Linux kernel's own measurements: */
668 case INITRD_EVENT_TAG_ID
:
669 /* The tagged event payload is just a constant string, hence don't show it */
670 if (!strextend_with_separator(&rec
->description
, ", ", "Linux: initrd"))
674 case LOAD_OPTIONS_EVENT_TAG_ID
:
676 if (!strextend_with_separator(&rec
->description
, ", ", "Linux: kernel command line"))
680 /* systemd's measurements: */
681 case LOADER_CONF_EVENT_TAG_ID
:
683 if (!strextend_with_separator(&rec
->description
, ", ", "systemd-boot: loader.conf"))
687 case DEVICETREE_ADDON_EVENT_TAG_ID
: {
688 _cleanup_free_
char *raw
= NULL
, *s
= NULL
;
690 raw
= utf16_to_utf8((const char16_t
*) tag
->taggedEventData
, tag
->taggedEventDataSize
);
698 r
= strextendf_with_separator(&rec
->description
, ", ", "systemd-stub: devicetree addon %s", s
);
700 return log_error_errno(r
, "Failed to format EV_EVENT_TAG description string: %m");
705 _cleanup_free_
char *s
= NULL
;
707 s
= cescape_length((char*) tag
->taggedEventData
, tag
->taggedEventDataSize
);
711 r
= strextendf_with_separator(&rec
->description
, ", ", "Tag 0x%" PRIx32
": %s", tag
->taggedEventID
, s
);
713 return log_error_errno(r
, "Failed to format EV_EVENT_TAG description string: %m");
718 tag
= (TCG_PCClientTaggedEvent
*) ((uint8_t*) tag
+ m
);
728 case EV_EFI_PLATFORM_FIRMWARE_BLOB
: {
729 const UEFI_PLATFORM_FIRMWARE_BLOB
*blob
;
730 if (rec
->firmware_payload_size
!= sizeof(UEFI_PLATFORM_FIRMWARE_BLOB
)) {
731 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "EV_EFI_PLATFORM_FIRMWARE_BLOB of wrong size, ignoring.");
735 blob
= rec
->firmware_payload
;
736 if (asprintf(&rec
->description
, "Blob: %s @ 0x%" PRIx64
, FORMAT_BYTES(blob
->blobLength
), blob
->blobBase
) < 0)
742 case EV_EFI_BOOT_SERVICES_APPLICATION
:
743 case EV_EFI_BOOT_SERVICES_DRIVER
:
744 case EV_EFI_RUNTIME_SERVICES_DRIVER
: {
745 const UEFI_IMAGE_LOAD_EVENT
*load
;
746 _cleanup_free_
char *fn
= NULL
;
749 if (rec
->firmware_payload_size
< offsetof(UEFI_IMAGE_LOAD_EVENT
, devicePath
)) {
750 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path too short, ignoring.");
754 load
= rec
->firmware_payload
;
755 if (load
->lengthOfDevicePath
!=
756 rec
->firmware_payload_size
- offsetof(UEFI_IMAGE_LOAD_EVENT
, devicePath
)) {
757 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path size does not match, ignoring.");
761 const packed_EFI_DEVICE_PATH
*dp
= (const packed_EFI_DEVICE_PATH
*) load
->devicePath
;
762 size_t left
= load
->lengthOfDevicePath
;
767 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Garbage after device path end, ignoring.");
775 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Garbage after device path end, ignoring.");
779 if (left
< offsetof(packed_EFI_DEVICE_PATH
, path
) || left
< dp
->length
) {
780 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path element too short, ignoring.");
784 if (dp
->type
== 4 && dp
->subType
== 4) {
785 /* Filename, store the last node of this type as description, it should contain the file name */
788 fn
= utf16_to_utf8((void*) dp
->path
, dp
->length
- offsetof(packed_EFI_DEVICE_PATH
, path
));
792 } else if (dp
->type
== 0x7F && dp
->subType
== 0xFF)
793 /* End of Hardware Device Path */
796 log_debug("Ignoring device path element type=0x%02x subtype=0x%02x", dp
->type
, dp
->subType
);
799 dp
= (packed_EFI_DEVICE_PATH
*) ((uint8_t*) dp
+ dp
->length
);
803 rec
->description
= strjoin("File: ", fn
);
804 if (!rec
->description
)
814 /* Catchall: show binary data */
815 fallback
= cescape_length(rec
->firmware_payload
, rec
->firmware_payload_size
);
819 rec
->description
= strjoin("Raw: ", fallback
);
820 if (!rec
->description
)
826 /* Mark the payload as invalid, so that we do not bother parsing/validating it any further */
827 rec
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
831 static int event_log_add_algorithms_from_record(EventLog
*el
, EventLogRecord
*record
) {
837 if (el
->algorithms_locked
)
840 LIST_FOREACH(banks
, i
, record
->banks
) {
841 r
= event_log_add_algorithm(el
, i
->algorithm
);
849 static int event_log_load_firmware(EventLog
*el
) {
850 const TCG_EfiSpecIdEventAlgorithmSize
*algorithms
;
851 size_t bufsize
= 0, n_algorithms
= 0, left
= 0;
852 _cleanup_free_
void *buf
= NULL
;
853 const TCG_PCR_EVENT2
*event
;
859 path
= tpm2_firmware_log_path();
861 r
= read_full_file(path
, (char**) &buf
, &bufsize
);
863 return log_error_errno(r
, "Failed to open TPM2 event log '%s': %m", path
);
866 /* Sometimes it's useful to invoke things with SYSTEMD_MEASURE_LOG_FIRMWARE=/dev/null, let's allow that, and proceed */
867 log_warning("Empty firmware event log file, not loading.");
871 r
= validate_firmware_header(buf
, bufsize
, &algorithms
, &n_algorithms
, &event
, &left
);
875 for (const TCG_PCR_EVENT2
*next_event
= NULL
;; event
= next_event
) {
876 EventLogRecord
*record
= NULL
;
880 r
= validate_firmware_event(
894 if (event
->eventType
== EV_NO_ACTION
&&
895 event
->pcrIndex
== 0 &&
896 payload_size
== 17 &&
897 memcmp(payload
, "StartupLocality", sizeof("StartupLocality")) == 0) {
898 if (el
->startup_locality_found
)
899 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "StartupLocality event found twice!");
901 el
->startup_locality
= ((const uint8_t*) payload
)[sizeof("StartupLocality")];
902 el
->startup_locality_found
= true;
903 log_debug("Found StartupLocality event: %u", el
->startup_locality
);
907 if (event
->eventType
== EV_NO_ACTION
) { /* Ignore pseudo events, that don't result in a measurement */
908 log_debug("Skipping NO_ACTION event.");
912 r
= event_log_add_record(el
, &record
);
914 return log_error_errno(r
, "Failed to add record to event log: %m");
916 record
->pcr
= event
->pcrIndex
;
917 record
->source
= path
;
918 record
->firmware_event_type
= event
->eventType
;
919 record
->firmware_payload
= memdup(payload
, payload_size
);
920 if (!record
->firmware_payload
)
922 record
->firmware_payload_size
= payload_size
;
924 const void *ha
, *ha_next
= NULL
;
925 ha
= (const uint8_t*) event
+ offsetof(TCG_PCR_EVENT2
, digests
.digests
);
926 assert(event
->digests
.count
== n_algorithms
);
928 for (size_t i
= 0; i
< n_algorithms
; i
++, ha
= ha_next
) {
929 ha_next
= (const uint8_t*) ha
+ offsetof(TPMT_HA
, digest
) + algorithms
[i
].digestSize
;
931 /* The TPMT_HA is not aligned in the record, hence read the hashAlg field via an unaligned read */
932 assert_cc(__builtin_types_compatible_p(uint16_t, typeof(TPMI_ALG_HASH
)));
933 uint16_t hash_alg
= unaligned_read_ne16((const uint8_t*) ha
+ offsetof(TPMT_HA
, hashAlg
));
935 if (hash_alg
!= algorithms
[i
].algorithmId
)
936 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Hash algorithms in event log record don't match log.");
938 if (!tpm2_hash_alg_to_string(algorithms
[i
].algorithmId
))
941 r
= event_log_record_add_bank(
943 algorithms
[i
].algorithmId
,
944 (const uint8_t*) ha
+ offsetof(TPMT_HA
, digest
),
945 algorithms
[i
].digestSize
,
948 return log_error_errno(r
, "Failed to add bank to event log record: %m");
951 /* Try to extract a descriptive text */
952 r
= event_log_record_extract_firmware_description(record
);
956 r
= event_log_add_algorithms_from_record(el
, record
);
964 static int event_log_record_parse_json(EventLogRecord
*record
, JsonVariant
*j
) {
965 const char *rectype
= NULL
;
973 if (!json_variant_is_object(j
))
974 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "record object is not an object.");
976 x
= json_variant_by_key(j
, "pcr");
978 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field missing from TPM measurement log file entry.");
979 if (!json_variant_is_unsigned(x
))
980 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field is not an integer.");
982 u
= json_variant_unsigned(x
);
983 if (u
>= TPM2_PCRS_MAX
)
984 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field is out of range.");
985 record
->pcr
= json_variant_unsigned(x
);
987 x
= json_variant_by_key(j
, "digests");
989 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field missing from TPM measurement log file entry.");
990 if (!json_variant_is_array(x
))
991 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field is not an array.");
993 JSON_VARIANT_ARRAY_FOREACH(k
, x
) {
994 _cleanup_free_
void *hash
= NULL
;
999 a
= json_variant_by_key(k
, "hashAlg");
1001 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field element lacks 'hashAlg' field.");
1002 if (!json_variant_is_string(a
))
1003 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'hashAlg' field is not a string.");
1005 na
= tpm2_hash_alg_from_string(json_variant_string(a
));
1007 log_debug_errno(na
, "Unsupported hash '%s' in userspace event log, ignoring: %m", json_variant_string(a
));
1011 h
= json_variant_by_key(k
, "digest");
1013 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field lacks 'digest' field");
1015 r
= json_variant_unhex(h
, &hash
, &hash_size
);
1017 return log_error_errno(r
, "Failed to decode digest: %m");
1019 r
= event_log_record_add_bank(
1026 return log_error_errno(r
, "Failed to add bank to event log record: %m");
1029 x
= json_variant_by_key(j
, "content_type");
1031 log_debug("'content_type' missing from TPM measurement log file entry, ignoring.");
1033 if (!json_variant_is_string(x
))
1034 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content_type' field is not a string.");
1036 rectype
= json_variant_string(x
);
1039 if (streq_ptr(rectype
, "systemd")) {
1042 x
= json_variant_by_key(j
, "content");
1044 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content' field missing from TPM measurement log file entry.");
1045 if (!json_variant_is_object(x
))
1046 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content' sub-object is not an object.");
1048 y
= json_variant_by_key(x
, "string");
1050 if (!json_variant_is_string(y
))
1051 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'string' field is not a string.");
1053 r
= free_and_strdup_warn(&record
->description
, json_variant_string(y
));
1058 y
= json_variant_by_key(x
, "eventType");
1060 if (!json_variant_is_string(y
))
1061 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'eventType' field is not a string.");
1063 record
->userspace_event_type
= tpm2_userspace_event_type_from_string(json_variant_string(y
));
1064 if (record
->userspace_event_type
< 0)
1065 log_debug_errno(record
->userspace_event_type
, "Unknown userspace event type '%s', ignoring.", json_variant_string(y
));
1068 json_variant_unref(record
->userspace_content
);
1069 record
->userspace_content
= json_variant_ref(x
);
1075 static int event_log_load_userspace(EventLog
*el
) {
1076 _cleanup_fclose_
FILE *f
= NULL
;
1077 _cleanup_free_
char *b
= NULL
;
1078 bool beginning
= true;
1085 path
= tpm2_userspace_log_path();
1087 f
= fopen(path
, "re");
1089 if (errno
!= ENOENT
)
1090 return log_error_errno(errno
, "Failed to open userspace TPM measurement log file: %m");
1095 if (flock(fileno(f
), LOCK_SH
) < 0)
1096 return log_error_errno(errno
, "Failed to lock userspace TPM measurement log file: %m");
1099 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
1100 EventLogRecord
*record
;
1106 return log_error_errno(errno
, "Failed to read local TPM measurement log file: %m");
1110 } else if (ch
!= 0x1EU
) {
1111 if (!GREEDY_REALLOC(b
, bn
+ 2))
1114 b
[bn
++] = (char) ch
;
1123 if (!GREEDY_REALLOC(b
, bn
+ 1))
1126 b
[bn
] = 0; /* Turn it into a string */
1128 if (memchr(b
, 0, bn
)) {
1129 log_warning("Found record with embedded NUL byte, skipping.");
1133 r
= json_parse(b
, 0, &j
, NULL
, NULL
);
1135 return log_error_errno(r
, "Failed to parse local TPM measurement log file: %m");
1137 r
= event_log_add_record(el
, &record
);
1139 return log_error_errno(r
, "Failed to add record to event log: %m");
1141 record
->source
= path
;
1143 r
= event_log_record_parse_json(record
, j
);
1147 r
= event_log_add_algorithms_from_record(el
, record
);
1161 static EventLog
*event_log_new(void) {
1162 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
1164 el
= new(EventLog
, 1);
1169 .primary_algorithm
= UINT16_MAX
,
1172 return TAKE_PTR(el
);
1175 static int event_log_load(EventLog
*el
) {
1180 r
= event_log_load_firmware(el
);
1184 r
= event_log_load_userspace(el
);
1191 static int event_log_read_pcrs(EventLog
*el
) {
1192 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
1197 r
= tpm2_context_new_or_warn(/* device= */ NULL
, &tc
);
1201 FOREACH_ARRAY(rr
, el
->registers
, TPM2_PCRS_MAX
) {
1205 rr
->banks
= new0(EventLogRegisterBank
, el
->n_algorithms
);
1210 for (size_t a
= 0; a
< el
->n_algorithms
; a
++) {
1211 _cleanup_free_ Tpm2PCRValue
*pcr_values
= NULL
;
1212 size_t n_pcr_values
;
1213 TPML_PCR_SELECTION selection
;
1215 tpm2_tpml_pcr_selection_from_mask(TPM2_PCRS_MASK
, el
->algorithms
[a
], &selection
);
1216 r
= tpm2_pcr_read(tc
, &selection
, &pcr_values
, &n_pcr_values
);
1220 FOREACH_ARRAY(v
, pcr_values
, n_pcr_values
) {
1221 assert(v
->hash
== el
->algorithms
[a
]);
1222 el
->registers
[v
->index
].banks
[a
].observed
= v
->value
;
1229 static void event_log_initial_pcr_state(EventLog
*el
, uint32_t pcr
, size_t size
, TPM2B_DIGEST
*ret
) {
1231 assert(pcr
< TPM2_PCRS_MAX
);
1233 assert(size
<= sizeof_field(TPM2B_DIGEST
, buffer
));
1241 memzero(ret
->buffer
, ret
->size
-1);
1242 ((uint8_t*) ret
->buffer
)[ret
->size
-1] = el
->startup_locality_found
? el
->startup_locality
: 0;
1247 memzero(ret
->buffer
, ret
->size
);
1251 memset(ret
->buffer
, 0xffu
, ret
->size
);
1255 assert_not_reached();
1259 static int event_log_calculate_pcrs(EventLog
*el
) {
1262 /* Iterates through the event log an calculates the expected hash values based on all listed records */
1265 el
->mds
= new(const EVP_MD
*, el
->n_algorithms
);
1269 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1273 assert_se(a
= tpm2_hash_alg_to_string(el
->algorithms
[i
]));
1274 assert_se(md
= EVP_get_digestbyname(a
));
1279 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++)
1280 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1281 EventLogRegisterBank
*b
= el
->registers
[pcr
].banks
+ i
;
1282 event_log_initial_pcr_state(el
, pcr
, EVP_MD_size(el
->mds
[i
]), &b
->calculated
);
1285 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1286 EventLogRegister
*reg
= el
->registers
+ (*rr
)->pcr
;
1288 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1289 const char *n
= tpm2_hash_alg_to_string(el
->algorithms
[i
]);
1290 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*mc
= NULL
;
1291 EventLogRegisterBank
*reg_b
;
1292 EventLogRecordBank
*rec_b
;
1295 rec_b
= event_log_record_find_bank(*rr
, el
->algorithms
[i
]);
1297 log_warning_errno(SYNTHETIC_ERRNO(ENXIO
), "Record with missing bank '%s', ignoring.", n
);
1301 reg_b
= reg
->banks
+ i
;
1303 mc
= EVP_MD_CTX_new();
1307 if (EVP_DigestInit_ex(mc
, el
->mds
[i
], NULL
) != 1)
1308 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to initialize %s message digest context.", n
);
1310 if (EVP_DigestUpdate(mc
, reg_b
->calculated
.buffer
, reg_b
->calculated
.size
) != 1)
1311 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
1313 if (EVP_DigestUpdate(mc
, rec_b
->hash
.buffer
, rec_b
->hash
.size
) != 1)
1314 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
1316 if (EVP_DigestFinal_ex(mc
, reg_b
->calculated
.buffer
, &sz
) != 1)
1317 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
1319 assert(sz
== reg_b
->calculated
.size
);
1322 reg
->n_measurements
++;
1328 static int event_log_record_validate_hash_firmware(
1329 EventLogRecord
*record
,
1330 EventLogRecordBank
*bank
,
1333 _cleanup_free_
void *hdata_alternative
= NULL
;
1334 size_t hsz
, hsz_alternative
= 0;
1335 bool strict
= false;
1342 if (!EVENT_LOG_RECORD_IS_FIRMWARE(record
))
1345 switch (record
->firmware_event_type
) {
1348 case EV_EFI_GPT_EVENT
:
1349 case EV_EFI_VARIABLE_BOOT2
:
1350 case EV_EFI_VARIABLE_DRIVER_CONFIG
:
1351 case EV_EFI_VARIABLE_AUTHORITY
:
1353 case EV_S_CRTM_VERSION
:
1354 /* Here the extended hash value is the hash value of the event payload. Note that
1355 * EV_PLATFORM_CONFIG_FLAGS (according to the TCG PC Client Platform Firmware Profile
1356 * Specification) is also supposed to be like this. But ovmf doesn't follow this requirement,
1357 * hence be lenient on that one, and don't include it here. */
1358 hdata
= record
->firmware_payload
;
1359 hsz
= record
->firmware_payload_size
;
1363 case EV_EFI_VARIABLE_BOOT
: {
1364 const UEFI_VARIABLE_DATA
*vdata
= record
->firmware_payload
;
1367 /* Here the extended hash value is the hash value of the variable data (i.e. excluding the
1370 * Note: we already checked the general validity of the UEFI_VARIABLE_DATA structure, hence
1371 * no need to do so again. */
1373 assert(record
->firmware_payload_size
>= offsetof(UEFI_VARIABLE_DATA
, unicodeName
));
1374 skip
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + vdata
->unicodeNameLength
* 2;
1376 assert(record
->firmware_payload_size
>= skip
);
1377 hdata
= (const uint8_t*) record
->firmware_payload
+ skip
;
1378 hsz
= record
->firmware_payload_size
- skip
;
1384 if (event_log_record_is_stub(record
)) {
1385 /* The PE section names have a descriptive string in UTF-16 in the payload, but the
1386 * hash is over the UTF-8 version (with suffixing 0), hence let's convert the payload
1387 * into that format here, and see if it checks out. */
1388 hdata_alternative
= utf16_to_utf8(record
->firmware_payload
, record
->firmware_payload_size
);
1389 if (!hdata_alternative
)
1392 hsz_alternative
= strlen(hdata_alternative
) + 1; /* with NUL byte */
1398 /* For the others check the data too, just in case. But usually this will not match, hence
1399 * only report if the checksum matches, but don't complain if it does not. */
1400 hdata
= record
->firmware_payload
;
1401 hsz
= record
->firmware_payload_size
;
1406 int mdsz
= EVP_MD_size(md
);
1408 assert((size_t) mdsz
<= sizeof_field(TPM2B_DIGEST
, buffer
));
1410 TPM2B_DIGEST payload_hash
= {
1414 unsigned dsz
= mdsz
;
1416 if (EVP_Digest(hdata
, hsz
, payload_hash
.buffer
, &dsz
, md
, NULL
) != 1)
1417 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1418 assert(dsz
== (unsigned) mdsz
);
1420 /* If this didn't match then let's try the alternative format here, if we have one, and check things then. */
1421 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
.buffer
, payload_hash
.size
) != 0 && hdata_alternative
) {
1422 if (EVP_Digest(hdata_alternative
, hsz_alternative
, payload_hash
.buffer
, &dsz
, md
, NULL
) != 1)
1423 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1424 assert(dsz
== (unsigned) mdsz
);
1427 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
.buffer
, payload_hash
.size
) != 0) {
1429 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
1430 else if (record
->event_payload_valid
!= EVENT_PAYLOAD_VALID_NO
)
1431 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_DONT_KNOW
;
1432 } else if (record
->event_payload_valid
< 0)
1433 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_YES
;
1438 static int event_log_record_validate_hash_userspace(
1439 EventLogRecord
*record
,
1440 EventLogRecordBank
*bank
,
1443 _cleanup_free_
unsigned char *payload_hash
= NULL
;
1444 unsigned payload_hash_size
;
1453 if (!EVENT_LOG_RECORD_IS_USERSPACE(record
))
1456 if (!record
->userspace_content
)
1459 js
= json_variant_by_key(record
->userspace_content
, "string");
1463 assert(json_variant_is_string(js
));
1464 s
= json_variant_string(js
);
1466 mdsz
= EVP_MD_size(md
);
1469 payload_hash_size
= mdsz
;
1470 payload_hash
= malloc(payload_hash_size
);
1474 if (EVP_Digest(s
, strlen(s
), payload_hash
, &payload_hash_size
, md
, NULL
) != 1)
1475 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1477 assert((int) payload_hash_size
== mdsz
);
1478 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
, payload_hash_size
) != 0)
1479 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
1480 else if (record
->event_payload_valid
< 0)
1481 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_YES
;
1486 static int event_log_validate_record_hashes(EventLog
*el
) {
1491 /* For records which contain the full data to validate the hashes, do so. */
1493 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1495 LIST_FOREACH(banks
, bank
, (*rr
)->banks
) {
1499 assert_se(a
= tpm2_hash_alg_to_string(bank
->algorithm
));
1500 assert_se(md
= EVP_get_digestbyname(a
));
1502 r
= event_log_record_validate_hash_firmware(*rr
, bank
, md
);
1506 r
= event_log_record_validate_hash_userspace(*rr
, bank
, md
);
1515 static int event_log_component_cmp(EventLogComponent
*const*a
, EventLogComponent
*const*b
) {
1516 const EventLogComponent
*x
= ASSERT_PTR(*ASSERT_PTR(a
)), *y
= ASSERT_PTR(*ASSERT_PTR(b
));
1518 return strcmp(x
->id
, y
->id
);
1521 static EventLogComponent
*event_log_find_component(EventLog
*el
, const char *id
) {
1522 EventLogComponent k
= {
1525 EventLogComponent
*kk
= &k
, **found
;
1530 found
= typesafe_bsearch(
1534 event_log_component_cmp
);
1541 static int event_log_add_component(EventLog
*el
, const char *id
, EventLogComponent
**ret
) {
1542 _cleanup_(event_log_component_freep
) EventLogComponent
*component
= NULL
;
1543 _cleanup_free_
char *id_copy
= NULL
;
1544 EventLogComponent
*found
;
1549 found
= event_log_find_component(el
, id
);
1555 if (!GREEDY_REALLOC(el
->components
, el
->n_components
+1))
1558 id_copy
= strdup(id
);
1562 component
= new(EventLogComponent
, 1);
1566 *component
= (EventLogComponent
) {
1567 .id
= TAKE_PTR(id_copy
),
1573 el
->components
[el
->n_components
++] = TAKE_PTR(component
);
1577 static int event_log_record_equal(const EventLogRecord
*a
, const EventLogRecord
*b
) {
1578 EventLogRecordBank
*x
, *y
;
1581 assert(a
->event_log
);
1583 assert(b
->event_log
);
1584 assert(a
->event_log
== b
->event_log
);
1586 if (a
->pcr
!= b
->pcr
)
1589 x
= event_log_record_find_bank(a
, a
->event_log
->primary_algorithm
);
1590 y
= event_log_record_find_bank(b
, b
->event_log
->primary_algorithm
);
1594 assert(x
->algorithm
== a
->event_log
->primary_algorithm
);
1595 assert(y
->algorithm
== b
->event_log
->primary_algorithm
);
1597 return memcmp_nn(x
->hash
.buffer
, x
->hash
.size
, y
->hash
.buffer
, y
->hash
.size
) == 0;
1600 static int event_log_add_component_file(EventLog
*el
, EventLogComponent
*component
, const char *path
) {
1601 _cleanup_(event_log_component_variant_freep
) EventLogComponentVariant
*variant
= NULL
;
1602 _cleanup_free_
char *fname
= NULL
, *id
= NULL
, *path_copy
= NULL
;
1603 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
1604 JsonVariant
*records
;
1610 r
= path_extract_filename(path
, &fname
);
1612 return log_error_errno(r
, "Failed to extract basename from path %s: %m", path
);
1614 e
= endswith(fname
, ".pcrlock");
1616 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Bad suffix: %s", fname
);
1618 id
= strndup(fname
, e
- fname
);
1623 r
= event_log_add_component(el
, id
, &component
);
1628 if (!GREEDY_REALLOC(component
->variants
, component
->n_variants
+1))
1631 r
= json_parse_file(
1636 /* ret_line= */ NULL
,
1637 /* ret_column= */ NULL
);
1639 log_warning_errno(r
, "Failed to parse component file %s, ignoring: %m", path
);
1643 if (!json_variant_is_object(j
)) {
1644 log_warning_errno(r
, "Component file %s does not contain JSON object, ignoring.", path
);
1648 path_copy
= strdup(path
);
1652 variant
= new(EventLogComponentVariant
, 1);
1656 *variant
= (EventLogComponentVariant
) {
1657 .component
= component
,
1658 .path
= TAKE_PTR(path_copy
),
1662 records
= json_variant_by_key(j
, "records");
1666 if (!json_variant_is_array(records
)) {
1667 log_warning_errno(r
, "Component records field of file %s is not an array, ignoring.", path
);
1671 JSON_VARIANT_ARRAY_FOREACH(rj
, records
) {
1672 _cleanup_(event_log_record_freep
) EventLogRecord
*record
= NULL
;
1674 if (!GREEDY_REALLOC(variant
->records
, variant
->n_records
+1))
1677 record
= event_log_record_new(el
);
1681 r
= event_log_record_parse_json(record
, rj
);
1685 record
->owning_component_variant
= variant
;
1686 variant
->records
[variant
->n_records
++] = TAKE_PTR(record
);
1690 component
->variants
[component
->n_variants
++] = TAKE_PTR(variant
);
1694 static int event_log_add_component_dir(EventLog
*el
, const char *path
, char **base_search
) {
1695 _cleanup_free_
char *fname
= NULL
, *id
= NULL
;
1696 _cleanup_strv_free_
char **files
= NULL
;
1697 EventLogComponent
*component
;
1703 r
= path_extract_filename(path
, &fname
);
1705 return log_error_errno(r
, "Failed to extract basename from path %s: %m", path
);
1707 e
= endswith(fname
, ".pcrlock.d");
1709 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Bad suffix: %s", fname
);
1711 id
= strndup(fname
, e
- fname
);
1715 r
= event_log_add_component(el
, id
, &component
);
1719 _cleanup_strv_free_
char **search
= NULL
;
1721 STRV_FOREACH(b
, base_search
) {
1722 _cleanup_free_
char *q
= NULL
;
1724 q
= path_join(*b
, fname
);
1728 r
= strv_consume(&search
, TAKE_PTR(q
));
1733 r
= conf_files_list_strv(&files
, ".pcrlock", /* root= */ NULL
, CONF_FILES_REGULAR
, (const char*const*) search
);
1735 return log_error_errno(r
, "Failed to enumerate .pcrlock files for component '%s': %m", id
);
1737 STRV_FOREACH(f
, files
) {
1738 r
= event_log_add_component_file(el
, component
, *f
);
1746 static int event_log_load_components(EventLog
*el
) {
1747 _cleanup_strv_free_
char **files
= NULL
;
1753 dirs
= arg_components
?:
1754 STRV_MAKE("/etc/pcrlock.d",
1756 "/var/lib/pcrlock.d",
1757 "/usr/local/lib/pcrlock.d",
1758 "/usr/lib/pcrlock.d");
1760 r
= conf_files_list_strv(&files
, NULL
, NULL
, CONF_FILES_REGULAR
|CONF_FILES_DIRECTORY
|CONF_FILES_FILTER_MASKED
, (const char*const*) dirs
);
1762 return log_error_errno(r
, "Failed to enumerate .pcrlock files: %m");
1764 STRV_FOREACH(f
, files
) {
1765 if (endswith(*f
, ".pcrlock.d"))
1766 r
= event_log_add_component_dir(el
, *f
, dirs
);
1767 else if (endswith(*f
, ".pcrlock"))
1768 r
= event_log_add_component_file(el
, NULL
, *f
);
1778 static int event_log_validate_fully_recognized(EventLog
*el
) {
1780 for (uint32_t pcr
= 0; pcr
< ELEMENTSOF(el
->registers
); pcr
++) {
1781 bool fully_recognized
= true;
1783 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1784 EventLogRecord
*rec
= *rr
;
1786 if (rec
->pcr
!= pcr
)
1789 if (rec
->n_mapped
== 0) {
1790 log_notice("Event log record %zu (PCR %" PRIu32
", \"%s\") not matching any component.",
1791 (size_t) (rr
- el
->records
), rec
->pcr
, strna(rec
->description
));
1792 fully_recognized
= false;
1797 el
->registers
[pcr
].fully_recognized
= fully_recognized
;
1803 static int event_log_match_component_variant(
1806 EventLogComponentVariant
*variant
,
1815 /* It's OK to point immediately after the last record, but not further */
1816 assert(i
<= el
->n_records
);
1817 assert(j
<= variant
->n_records
);
1819 /* All entries in the variant checked out? Yippieh! */
1820 if (j
== variant
->n_records
)
1823 /* If the remainder of the variant is longer than the remainder of the event log, it cannot possibly fit. */
1824 if (el
->n_records
- i
< variant
->n_records
- j
)
1827 /* Does this record match? If not, let's try at the next place in the logs. */
1828 if (!event_log_record_equal(el
->records
[i
], variant
->records
[j
]))
1829 return event_log_match_component_variant(el
, i
+ 1, variant
, j
, assign
); /* Recursion! */
1831 /* This one matches. Good. Let's see if the rest also matches. (Recursion!) */
1832 r
= event_log_match_component_variant(el
, i
+ 1, variant
, j
+ 1, assign
);
1837 /* Take ownership (Note we allow multiple components and variants to take owneship of the same record!) */
1838 if (!GREEDY_REALLOC(el
->records
[i
]->mapped
, el
->records
[i
]->n_mapped
+1))
1841 el
->records
[i
]->mapped
[el
->records
[i
]->n_mapped
++] = variant
;
1847 static uint32_t event_log_component_variant_pcrs(EventLogComponentVariant
*i
) {
1852 /* returns mask of PCRs touched by this variant */
1854 FOREACH_ARRAY(rr
, i
->records
, i
->n_records
)
1855 mask
|= UINT32_C(1) << (*rr
)->pcr
;
1860 static uint32_t event_log_component_pcrs(EventLogComponent
*c
) {
1865 /* Returns mask of PCRs touched by this component */
1867 FOREACH_ARRAY(ii
, c
->variants
, c
->n_variants
)
1868 mask
|= event_log_component_variant_pcrs(*ii
);
1873 static int event_log_map_components(EventLog
*el
) {
1874 _cleanup_free_
char *skipped_ids
= NULL
;
1875 unsigned n_skipped
= 0;
1880 FOREACH_ARRAY(cc
, el
->components
, el
->n_components
) {
1881 _cleanup_free_
char *matching_ids
= NULL
;
1882 unsigned n_matching
= 0, n_empty
= 0;
1883 EventLogComponent
*c
= *cc
;
1885 if (arg_location_end
&& strcmp(c
->id
, arg_location_end
) > 0) {
1888 if (!strextend_with_separator(&skipped_ids
, ", ", c
->id
))
1894 if (c
->n_variants
== 0)
1895 log_notice("Component '%s' has no defined variants.", c
->id
);
1897 FOREACH_ARRAY(ii
, c
->variants
, c
->n_variants
) {
1898 EventLogComponentVariant
*i
= *ii
;
1900 if (i
->n_records
== 0) {
1901 /* The empty variant always matches */
1906 r
= event_log_match_component_variant(el
, 0, i
, 0, n_matching
+ n_empty
== 0);
1912 if (!strextend_with_separator(&matching_ids
, ", ", i
->id
))
1917 if (n_matching
+ n_empty
== 0) {
1919 if (arg_location_start
&& strcmp(c
->id
, arg_location_start
) >= 0)
1920 log_info("Didn't find component '%s' in event log, assuming system hasn't reached it yet.", c
->id
);
1922 log_notice("Couldn't find component '%s' in event log.", c
->id
);
1923 el
->n_missing_components
++;
1924 el
->missing_component_pcrs
|= event_log_component_pcrs(c
);
1926 } else if (n_matching
> 1)
1927 log_debug("Found %u possible variants of component '%s' in event log (%s). Proceeding.", n_matching
, c
->id
, matching_ids
);
1931 log_notice("Skipped %u components after location '%s' (%s).", n_skipped
, arg_location_end
, skipped_ids
);
1932 if (el
->n_missing_components
> 0)
1933 log_notice("Unable to recognize %zu components in event log.", el
->n_missing_components
);
1935 return event_log_validate_fully_recognized(el
);
1938 static void hsv_to_rgb(
1939 double h
, double s
, double v
,
1940 uint8_t* ret_r
, uint8_t *ret_g
, uint8_t *ret_b
) {
1942 double c
, x
, m
, r
, g
, b
;
1944 assert(s
>= 0 && s
<= 100);
1945 assert(v
>= 0 && v
<= 100);
1950 c
= (s
/ 100.0) * (v
/ 100.0);
1951 x
= c
* (1 - fabs(fmod(h
/ 60.0, 2) - 1));
1954 if (h
>= 0 && h
< 60)
1955 r
= c
, g
= x
, b
= 0.0;
1956 else if (h
>= 60 && h
< 120)
1957 r
= x
, g
= c
, b
= 0.0;
1958 else if (h
>= 120 && h
< 180)
1959 r
= 0.0, g
= c
, b
= x
;
1960 else if (h
>= 180 && h
< 240)
1961 r
= 0.0, g
= x
, b
= c
;
1962 else if (h
>= 240 && h
< 300)
1963 r
= x
, g
= 0.0, b
= c
;
1965 r
= c
, g
= 0.0, b
= x
;
1967 *ret_r
= (uint8_t) ((r
+ m
) * 255);
1968 *ret_g
= (uint8_t) ((g
+ m
) * 255);
1969 *ret_b
= (uint8_t) ((b
+ m
) * 255);
1972 #define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
1974 static const char *ansi_true_color(uint8_t r
, uint8_t g
, uint8_t b
, char ret
[static ANSI_TRUE_COLOR_MAX
]) {
1975 snprintf(ret
, ANSI_TRUE_COLOR_MAX
, "\x1B[38;2;%u;%u;%um", r
, g
, b
);
1979 static char *color_for_pcr(EventLog
*el
, uint32_t pcr
) {
1980 char color
[ANSI_TRUE_COLOR_MAX
];
1984 assert(pcr
< TPM2_PCRS_MAX
);
1986 if (el
->registers
[pcr
].color
)
1987 return el
->registers
[pcr
].color
;
1989 hsv_to_rgb(360.0 / (TPM2_PCRS_MAX
- 1) * pcr
, 100, 90, &r
, &g
, &b
);
1990 ansi_true_color(r
, g
, b
, color
);
1992 el
->registers
[pcr
].color
= strdup(color
);
1993 return el
->registers
[pcr
].color
;
1996 static int add_algorithm_columns(
2000 const char *json_field_prefix
) {
2007 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2008 const char *n
= tpm2_hash_alg_to_string(*alg
);
2009 _cleanup_free_
char *v
= NULL
;
2012 v
= strjoin(prefix
, " ", n
);
2017 size_t c
= table_get_current_column(table
);
2019 r
= table_add_cell(table
, NULL
, TABLE_HEADER
, v
?: n
);
2021 return table_log_add_error(r
);
2023 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
) &&
2024 el
->primary_algorithm
!= UINT16_MAX
&&
2025 *alg
!= el
->primary_algorithm
)
2026 (void) table_hide_column_from_display(table
, c
);
2028 _cleanup_free_
char *j
= NULL
;
2029 if (json_field_prefix
) {
2030 _cleanup_free_
char *m
= strdup(n
);
2034 j
= strjoin(json_field_prefix
, ascii_strupper(m
));
2039 (void) table_set_json_field_name(table
, c
, j
?: n
);
2045 static int show_log_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2046 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2051 table
= table_new_raw(5 + el
->n_algorithms
+ 4);
2055 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2057 r
= table_add_many(table
,
2058 TABLE_HEADER
, "pcr",
2059 TABLE_SET_ALIGN_PERCENT
, 100,
2061 TABLE_HEADER
, "pcrname",
2062 TABLE_HEADER
, "event",
2063 TABLE_HEADER
, "match",
2064 TABLE_SET_ALIGN_PERCENT
, 100);
2066 return table_log_add_error(r
);
2068 r
= add_algorithm_columns(el
, table
, NULL
, NULL
);
2072 size_t phase_column
= table_get_current_column(table
);
2074 r
= table_add_many(table
,
2075 TABLE_HEADER
, "F/U",
2076 TABLE_HEADER
, "source",
2077 TABLE_HEADER
, "component",
2078 TABLE_HEADER
, "description");
2080 return table_log_add_error(r
);
2082 (void) table_hide_column_from_display(table
, table_get_columns(table
) - 3); /* hide source */
2084 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2085 (void) table_hide_column_from_display(table
, (size_t) 1); /* hide color block column */
2087 (void) table_set_json_field_name(table
, phase_column
, "phase");
2089 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2090 EventLogRecord
*record
= *rr
;
2092 r
= table_add_many(table
,
2093 TABLE_UINT32
, record
->pcr
,
2094 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2095 TABLE_SET_COLOR
, color_for_pcr(el
, record
->pcr
),
2096 TABLE_STRING
, tpm2_pcr_index_to_string(record
->pcr
));
2098 return table_log_add_error(r
);
2100 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2103 et
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2105 r
= table_add_cell(table
, NULL
, TABLE_STRING
, et
);
2107 r
= table_add_cell(table
, NULL
, TABLE_UINT32_HEX
, &record
->firmware_event_type
);
2108 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
))
2109 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tpm2_userspace_event_type_to_string(record
->userspace_event_type
));
2111 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2113 return table_log_add_error(r
);
2115 if (record
->event_payload_valid
< 0 || record
->event_payload_valid
== EVENT_PAYLOAD_VALID_DONT_KNOW
)
2116 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2118 r
= table_add_many(table
,
2119 TABLE_BOOLEAN_CHECKMARK
, record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
,
2120 TABLE_SET_COLOR
, ansi_highlight_green_red(record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
));
2122 return table_log_add_error(r
);
2124 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2125 EventLogRecordBank
*bank
;
2127 bank
= event_log_record_find_bank(record
, *alg
);
2129 _cleanup_free_
char *hex
= NULL
;
2131 hex
= hexmem(bank
->hash
.buffer
, bank
->hash
.size
);
2135 r
= table_add_cell(table
, NULL
, TABLE_STRING
, hex
);
2137 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2139 return table_log_add_error(r
);
2142 r
= table_add_many(table
,
2143 TABLE_STRING
, EVENT_LOG_RECORD_IS_FIRMWARE(record
) ? "F" :
2144 EVENT_LOG_RECORD_IS_USERSPACE(record
) ? "U" : NULL
,
2145 TABLE_PATH_BASENAME
, record
->source
,
2146 TABLE_PATH_BASENAME
, record
->n_mapped
> 0 ? record
->mapped
[0]->component
->id
: NULL
,
2147 TABLE_STRING
, record
->description
);
2149 return table_log_add_error(r
);
2153 r
= table_to_json(table
, ret_variant
);
2155 return log_error_errno(r
, "Failed to format table to JSON: %m");
2160 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */true);
2162 return log_error_errno(r
, "Failed to output table: %m");
2167 static bool is_unset_pcr(const void *value
, size_t size
) {
2168 return memeqzero(value
, size
) || memeqbyte(0xffu
, value
, size
);
2171 static bool event_log_pcr_checks_out(const EventLog
*el
, const EventLogRegister
*reg
) {
2175 for (size_t i
= 0; i
< el
->n_algorithms
; i
++)
2176 if (memcmp_nn(reg
->banks
[i
].calculated
.buffer
, reg
->banks
[i
].calculated
.size
,
2177 reg
->banks
[i
].observed
.buffer
, reg
->banks
[i
].observed
.size
) != 0)
2183 static int show_pcr_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2184 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2189 table
= table_new_raw(8 + el
->n_algorithms
*2);
2193 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2195 r
= table_add_many(table
,
2196 TABLE_HEADER
, "pcr",
2197 TABLE_SET_ALIGN_PERCENT
, 100,
2198 TABLE_HEADER
, "", /* color block column */
2199 TABLE_HEADER
, "", /* emoji column */
2200 TABLE_HEADER
, "pcrname",
2201 TABLE_HEADER
, "count",
2202 TABLE_SET_ALIGN_PERCENT
, 100,
2204 TABLE_SET_ALIGN_PERCENT
, 100,
2206 TABLE_SET_ALIGN_PERCENT
, 100,
2208 TABLE_SET_ALIGN_PERCENT
, 100);
2210 return table_log_add_error(r
);
2212 r
= add_algorithm_columns(el
, table
, "Calculated", "calculated");
2216 r
= add_algorithm_columns(el
, table
, "Observed", "observed");
2220 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2221 (void) table_hide_column_from_display(table
, (size_t) 1, (size_t) 2); /* hide color block and emoji column */
2222 else if (!emoji_enabled())
2223 (void) table_hide_column_from_display(table
, (size_t) 2);
2225 (void) table_set_json_field_name(table
, 5, "hashMatchesEventLog");
2226 (void) table_set_json_field_name(table
, 6, "allEventsMatched");
2227 (void) table_set_json_field_name(table
, 7, "noMissingComponents");
2229 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2230 /* Check if the PCR hash value matches the event log data */
2231 bool hash_match
= event_log_pcr_checks_out(el
, el
->registers
+ pcr
);
2233 /* Whether all records in this PCR have a matching component */
2234 bool fully_recognized
= el
->registers
[pcr
].fully_recognized
;
2236 /* Whether any unmatched components touch this PCR */
2237 bool missing_components
= FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
);
2239 const char *emoji
= special_glyph(
2240 !hash_match
? SPECIAL_GLYPH_DEPRESSED_SMILEY
:
2241 !fully_recognized
? SPECIAL_GLYPH_UNHAPPY_SMILEY
:
2242 missing_components
? SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
:
2243 SPECIAL_GLYPH_HAPPY_SMILEY
);
2245 r
= table_add_many(table
,
2247 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2248 TABLE_SET_COLOR
, color_for_pcr(el
, pcr
),
2249 TABLE_STRING
, emoji
,
2250 TABLE_STRING
, tpm2_pcr_index_to_string(pcr
));
2252 return table_log_add_error(r
);
2254 if (el
->registers
[pcr
].n_measurements
> 0)
2255 r
= table_add_cell(table
, NULL
, TABLE_UINT
, &el
->registers
[pcr
].n_measurements
);
2257 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2259 return table_log_add_error(r
);
2261 r
= table_add_many(table
,
2262 TABLE_BOOLEAN_CHECKMARK
, hash_match
,
2263 TABLE_SET_COLOR
, ansi_highlight_green_red(hash_match
),
2264 TABLE_BOOLEAN_CHECKMARK
, fully_recognized
,
2265 TABLE_SET_COLOR
, ansi_highlight_green_red(fully_recognized
),
2266 TABLE_BOOLEAN_CHECKMARK
, !missing_components
,
2267 TABLE_SET_COLOR
, ansi_highlight_green_red(!missing_components
));
2269 return table_log_add_error(r
);
2271 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2274 color
= is_unset_pcr(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
) ? ANSI_GREY
: NULL
;
2276 if (el
->registers
[pcr
].banks
[i
].calculated
.size
> 0) {
2277 _cleanup_free_
char *hex
= NULL
;
2279 hex
= hexmem(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
);
2283 r
= table_add_many(table
,
2285 TABLE_SET_COLOR
, color
);
2287 r
= table_add_many(table
,
2289 TABLE_SET_COLOR
, color
);
2291 return table_log_add_error(r
);
2294 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2295 _cleanup_free_
char *hex
= NULL
;
2298 hex
= hexmem(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
);
2302 color
= !hash_match
? ANSI_HIGHLIGHT_RED
:
2303 is_unset_pcr(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
) ? ANSI_GREY
: NULL
;
2305 r
= table_add_many(table
,
2307 TABLE_SET_COLOR
, color
);
2309 return table_log_add_error(r
);
2314 r
= table_to_json(table
, ret_variant
);
2316 return log_error_errno(r
, "Failed to format table to JSON: %m");
2321 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2323 return log_error_errno(r
, "Failed to output table: %m");
2325 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2327 "%sLegend: H → PCR hash value matches event log%s\n"
2328 "%s R → All event log records for this PCR have a matching component%s\n"
2329 "%s C → No components that couldn't be matched with log records affect this PCR%s\n",
2330 ansi_grey(), ansi_normal(), /* less on small screens automatically resets the color after long lines, hence we set it anew for each line */
2331 ansi_grey(), ansi_normal(),
2332 ansi_grey(), ansi_normal());
2337 static int event_determine_primary_algorithm(EventLog
*el
) {
2340 if (el
->n_algorithms
== 0) {
2341 /* Nothing loaded to make the decision on? Then pick SHA256 */
2342 el
->primary_algorithm
= TPM2_ALG_SHA256
;
2346 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2347 /* If we have SHA256, focus on that that */
2349 if (*alg
== TPM2_ALG_SHA256
) {
2350 el
->primary_algorithm
= *alg
;
2355 /* Otherwise show the "best" (i.e. the one with the highest id value) */
2356 el
->primary_algorithm
= el
->algorithms
[el
->n_algorithms
-1];
2360 static int event_log_load_and_process(EventLog
**ret
) {
2361 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2364 el
= event_log_new();
2368 r
= event_log_add_algorithms_from_environment(el
);
2372 r
= event_log_load(el
);
2376 r
= event_log_read_pcrs(el
);
2380 r
= event_log_calculate_pcrs(el
);
2384 r
= event_log_validate_record_hashes(el
);
2388 r
= event_determine_primary_algorithm(el
);
2392 r
= event_log_load_components(el
);
2396 r
= event_log_map_components(el
);
2400 *ret
= TAKE_PTR(el
);
2404 static int verb_show_log(int argc
, char *argv
[], void *userdata
) {
2405 _cleanup_(json_variant_unrefp
) JsonVariant
*log_table
= NULL
, *pcr_table
= NULL
;
2406 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2407 bool want_json
= !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
);
2410 r
= event_log_load_and_process(&el
);
2417 r
= show_log_table(el
, want_json
? &log_table
: NULL
);
2424 r
= show_pcr_table(el
, want_json
? &pcr_table
: NULL
);
2429 _cleanup_(json_variant_unrefp
) JsonVariant
*object
= NULL
;
2431 r
= json_build(&object
, JSON_BUILD_OBJECT(
2432 JSON_BUILD_PAIR_VARIANT("log", log_table
),
2433 JSON_BUILD_PAIR_VARIANT("pcrs", pcr_table
)));
2435 return log_error_errno(r
, "Failed to generate combined object: %m");
2437 r
= json_variant_dump(object
, arg_json_format_flags
, stdout
, /* prefix= */ NULL
);
2439 return log_error_errno(r
, "Failed to dump JSON object: %m");
2445 static int verb_show_cel(int argc
, char *argv
[], void *userdata
) {
2446 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2447 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2448 uint64_t recnum
= 0;
2451 el
= event_log_new();
2455 r
= event_log_load(el
);
2459 /* Output the event log in TCG CEL-JSON. */
2461 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2462 _cleanup_(json_variant_unrefp
) JsonVariant
*ja
= NULL
, *fj
= NULL
;
2463 EventLogRecord
*record
= *rr
;
2464 JsonVariant
*cd
= NULL
;
2465 const char *ct
= NULL
;
2467 LIST_FOREACH(banks
, bank
, record
->banks
) {
2468 r
= json_variant_append_arrayb(
2469 &ja
, JSON_BUILD_OBJECT(
2470 JSON_BUILD_PAIR_STRING("hashAlg", tpm2_hash_alg_to_string(bank
->algorithm
)),
2471 JSON_BUILD_PAIR_HEX("digest", bank
->hash
.buffer
, bank
->hash
.size
)));
2473 return log_error_errno(r
, "Failed to append CEL digest entry: %m");
2477 r
= json_variant_new_array(&ja
, NULL
, 0);
2479 return log_error_errno(r
, "Failed to allocate JSON array: %m");
2482 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2483 _cleanup_free_
char *et
= NULL
;
2486 z
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2488 _cleanup_free_
char *b
= NULL
;
2490 b
= strreplace(z
, "-", "_");
2494 et
= strjoin("EV_", ascii_strupper(b
));
2497 } else if (asprintf(&et
, "%" PRIu32
, record
->firmware_event_type
) < 0)
2500 r
= json_build(&fj
, JSON_BUILD_OBJECT(
2501 JSON_BUILD_PAIR_STRING("event_type", et
),
2502 JSON_BUILD_PAIR_HEX("event_data", record
->firmware_payload
, record
->firmware_payload_size
)));
2504 return log_error_errno(r
, "Failed to build firmware event data: %m");
2507 ct
= "pcclient_std";
2508 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
)) {
2509 cd
= record
->userspace_content
;
2513 r
= json_variant_append_arrayb(&array
,
2515 JSON_BUILD_PAIR_UNSIGNED("pcr", record
->pcr
),
2516 JSON_BUILD_PAIR_UNSIGNED("recnum", ++recnum
),
2517 JSON_BUILD_PAIR_VARIANT("digests", ja
),
2518 JSON_BUILD_PAIR_CONDITION(ct
, "content_type", JSON_BUILD_STRING(ct
)),
2519 JSON_BUILD_PAIR_CONDITION(cd
, "content", JSON_BUILD_VARIANT(cd
))));
2521 return log_error_errno(r
, "Failed to append CEL record: %m");
2524 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
2525 pager_open(arg_pager_flags
);
2527 json_variant_dump(array
, arg_json_format_flags
|JSON_FORMAT_EMPTY_ARRAY
, stdout
, NULL
);
2531 static int verb_list_components(int argc
, char *argv
[], void *userdata
) {
2532 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2533 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2538 } loc
= BEFORE_LOCATION
;
2541 el
= event_log_new();
2545 r
= event_log_add_algorithms_from_environment(el
);
2549 r
= event_determine_primary_algorithm(el
);
2553 r
= event_log_load_components(el
);
2557 table
= table_new("id", "variants");
2561 FOREACH_ARRAY(c
, el
->components
, el
->n_components
) {
2563 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2564 _cleanup_free_
char *marker
= NULL
;
2568 case BEFORE_LOCATION
:
2569 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2570 loc
= AFTER_LOCATION
;
2571 marker
= strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), " location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_LEFT
));
2572 } else if (arg_location_start
&& strcmp((*c
)->id
, arg_location_start
) >= 0) {
2573 loc
= BETWEEN_LOCATION
;
2574 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_TOP
), " start location '", arg_location_start
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_DOWN
));
2579 case BETWEEN_LOCATION
:
2580 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2581 loc
= AFTER_LOCATION
;
2582 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), " end location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_UP
));
2586 case AFTER_LOCATION
:
2591 r
= table_add_many(table
,
2592 TABLE_STRING
, marker
,
2593 TABLE_SET_COLOR
, ANSI_GREY
,
2596 return table_log_add_error(r
);
2600 FOREACH_ARRAY(variant
, (*c
)->variants
, (*c
)->n_variants
) {
2601 r
= table_add_many(table
,
2602 TABLE_STRING
, (*c
)->id
,
2603 TABLE_PATH
, (*variant
)->path
);
2605 return table_log_add_error(r
);
2609 if (table_get_rows(table
) > 1 || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2610 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2612 return log_error_errno(r
, "Failed to output table: %m");
2615 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2616 if (table_get_rows(table
) > 1)
2617 printf("\n%zu components listed.\n", table_get_rows(table
) - 1);
2619 printf("No components defined.\n");
2625 static int event_log_pcr_mask_checks_out(EventLog
*el
, uint32_t mask
) {
2628 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2630 if (!FLAGS_SET(mask
, UINT32_C(1) << pcr
))
2633 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
))
2634 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Event log for PCR %" PRIu32
" does not match PCR state, refusing.", pcr
);
2640 static int make_pcrlock_record(
2644 JsonVariant
**ret_record
) {
2646 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
2649 assert(data
|| data_size
== 0);
2652 if (data_size
== SIZE_MAX
)
2653 data_size
= strlen(data
);
2655 /* Generates a .pcrlock record for the given PCR and data/data size. This is a subset of TCG CEL. */
2657 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
2658 _cleanup_free_
unsigned char *hash
= NULL
;
2660 unsigned hash_usize
;
2664 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
2665 assert_se(md
= EVP_get_digestbyname(a
));
2666 hash_ssize
= EVP_MD_size(md
);
2667 assert_se(hash_ssize
> 0);
2668 hash_usize
= hash_ssize
;
2670 hash
= malloc(hash_usize
);
2674 if (EVP_Digest(data
, data_size
, hash
, &hash_usize
, md
, NULL
) != 1)
2675 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data with algorithm '%s'.", a
);
2677 r
= json_variant_append_arrayb(
2680 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
2681 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_usize
))));
2683 return log_error_errno(r
, "Failed to build JSON digest object: %m");
2686 r
= json_build(ret_record
,
2688 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(pcr
)),
2689 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
2691 return log_error_errno(r
, "Failed to build record object: %m");
2696 static const char *pcrlock_path(const char *default_pcrlock_path
) {
2697 return arg_pcrlock_path
?: arg_pcrlock_auto
? default_pcrlock_path
: NULL
;
2700 static int write_pcrlock(JsonVariant
*array
, const char *default_pcrlock_path
) {
2701 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *a
= NULL
;
2702 _cleanup_fclose_
FILE *f
= NULL
;
2707 r
= json_variant_new_array(&a
, NULL
, 0);
2709 return log_error_errno(r
, "Failed to allocate empty array: %m");
2714 r
= json_build(&v
, JSON_BUILD_OBJECT(
2715 JSON_BUILD_PAIR("records", JSON_BUILD_VARIANT(array
))));
2717 return log_error_errno(r
, "Failed to build JSON object: %m");
2719 p
= pcrlock_path(default_pcrlock_path
);
2721 (void) mkdir_parents_label(p
, 0755);
2725 return log_error_errno(errno
, "Failed to open %s for writing: %m", p
);
2728 r
= json_variant_dump(v
, arg_json_format_flags
, f
?: stdout
, /* prefix= */ NULL
);
2730 return log_error_errno(r
, "Failed to output JSON object: %m");
2733 log_info("%s written.", p
);
2738 static int unlink_pcrlock(const char *default_pcrlock_path
) {
2741 p
= pcrlock_path(default_pcrlock_path
);
2743 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No .pcrlock path specified, refusing.");
2745 if (unlink(p
) < 0) {
2746 if (errno
!= ENOENT
)
2747 return log_error_errno(errno
, "Failed to delete %s: %m", p
);
2749 log_info("%s already deleted.", p
);
2751 log_info("%s deleted.", p
);
2753 (void) rmdir_parents(p
, "/var/lib");
2758 static int verb_lock_raw(int argc
, char *argv
[], void *userdata
) {
2759 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2760 _cleanup_free_
char *data
= NULL
;
2761 _cleanup_fclose_
FILE *f
= NULL
;
2765 if (arg_pcr_mask
== 0)
2766 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No PCR specified, refusing.");
2769 f
= fopen(argv
[1], "re");
2771 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
2774 r
= read_full_stream(f
?: stdin
, &data
, &size
);
2776 return log_error_errno(r
, "Failed to read data from stdin: %m");
2778 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
2779 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2781 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
2784 r
= make_pcrlock_record(i
, data
, size
, &record
);
2788 r
= json_variant_append_array(&array
, record
);
2790 return log_error_errno(r
, "Failed to append to JSON array: %m");
2793 return write_pcrlock(array
, NULL
);
2796 static int verb_unlock_simple(int argc
, char *argv
[], void *userdata
) {
2797 return unlink_pcrlock(NULL
);
2800 static int verb_lock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2801 static const struct {
2804 int synthesize_empty
; /* 0 → fail, > 0 → synthesize empty db, < 0 → skip */
2806 { EFI_VENDOR_GLOBAL
, "SecureBoot", 0 },
2807 { EFI_VENDOR_GLOBAL
, "PK", 1 },
2808 { EFI_VENDOR_GLOBAL
, "KEK", 1 },
2809 { EFI_VENDOR_DATABASE
, "db", 1 },
2810 { EFI_VENDOR_DATABASE
, "dbx", 1 },
2811 { EFI_VENDOR_DATABASE
, "dbt", -1 },
2812 { EFI_VENDOR_DATABASE
, "dbr", -1 },
2815 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2818 /* Generates expected records from the current SecureBoot state, as readable in the EFI variables
2821 FOREACH_ARRAY(vv
, variables
, ELEMENTSOF(variables
)) {
2822 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2824 _cleanup_free_
char *name
= NULL
;
2825 if (asprintf(&name
, "%s-" SD_ID128_UUID_FORMAT_STR
, vv
->name
, SD_ID128_FORMAT_VAL(vv
->id
)) < 0)
2828 _cleanup_free_
void *data
= NULL
;
2830 r
= efi_get_variable(name
, NULL
, &data
, &data_size
);
2832 if (r
!= -ENOENT
|| vv
->synthesize_empty
== 0)
2833 return log_error_errno(r
, "Failed to read EFI variable '%s': %m", name
);
2834 if (vv
->synthesize_empty
< 0)
2837 /* If the main database variables are not set we don't consider this an error, but
2838 * measure an empty database instead. */
2839 log_debug("EFI variable %s is not set, synthesizing empty variable for measurement.", name
);
2843 _cleanup_free_ char16_t
* name16
= utf8_to_utf16(vv
->name
, SIZE_MAX
);
2846 size_t name16_bytes
= char16_strlen(name16
) * 2;
2848 size_t vdata_size
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + name16_bytes
+ data_size
;
2849 _cleanup_free_ UEFI_VARIABLE_DATA
*vdata
= malloc(vdata_size
);
2853 *vdata
= (UEFI_VARIABLE_DATA
) {
2854 .unicodeNameLength
= name16_bytes
/ 2,
2855 .variableDataLength
= data_size
,
2858 efi_id128_to_guid(vv
->id
, vdata
->variableName
);
2859 memcpy(mempcpy(vdata
->unicodeName
, name16
, name16_bytes
), data
, data_size
);
2861 r
= make_pcrlock_record(TPM2_PCR_SECURE_BOOT_POLICY
/* =7 */, vdata
, vdata_size
, &record
);
2865 r
= json_variant_append_array(&array
, record
);
2867 return log_error_errno(r
, "Failed to append to JSON array: %m");
2870 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_POLICY_PATH
);
2873 static int verb_unlock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2874 return unlink_pcrlock(PCRLOCK_SECUREBOOT_POLICY_PATH
);
2877 static int event_log_record_is_secureboot_variable(EventLogRecord
*rec
, sd_id128_t uuid
, const char *name
) {
2878 _cleanup_free_
char *found_name
= NULL
;
2879 sd_id128_t found_uuid
;
2885 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2888 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2891 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2894 if (rec
->firmware_event_type
!= EV_EFI_VARIABLE_DRIVER_CONFIG
)
2897 r
= event_log_record_parse_variable_data(rec
, &found_uuid
, &found_name
);
2903 if (!sd_id128_equal(found_uuid
, uuid
))
2906 return streq(found_name
, name
);
2909 static bool event_log_record_is_secureboot_authority(EventLogRecord
*rec
) {
2912 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2915 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2918 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2921 return rec
->firmware_event_type
== EV_EFI_VARIABLE_AUTHORITY
;
2924 static int event_log_ensure_secureboot_consistency(EventLog
*el
) {
2925 static const struct {
2930 { EFI_VENDOR_GLOBAL
, "SecureBoot", true },
2931 { EFI_VENDOR_GLOBAL
, "PK", true },
2932 { EFI_VENDOR_GLOBAL
, "KEK", true },
2933 { EFI_VENDOR_DATABASE
, "db", true },
2934 { EFI_VENDOR_DATABASE
, "dbx", true },
2935 { EFI_VENDOR_DATABASE
, "dbt", false },
2936 { EFI_VENDOR_DATABASE
, "dbr", false },
2937 // FIXME: ensure we also find the separator here
2940 EventLogRecord
*records
[ELEMENTSOF(table
)] = {};
2941 EventLogRecord
*first_authority
= NULL
;
2945 /* Ensures that the PCR 7 records are complete and in order. Before we lock down PCR 7 we want to
2946 * ensure its state is actually consistent. */
2948 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2949 EventLogRecord
*rec
= *rr
;
2950 size_t found
= SIZE_MAX
;
2952 if (event_log_record_is_secureboot_authority(rec
)) {
2953 if (first_authority
)
2956 first_authority
= rec
;
2957 // FIXME: also check that each authority record's data is also listed in 'db'
2961 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2962 if (event_log_record_is_secureboot_variable(rec
, table
[i
].id
, table
[i
].name
)) {
2966 if (found
== SIZE_MAX
)
2969 /* Require the authority records always come *after* database measurements */
2970 if (first_authority
)
2971 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "SecureBoot authority before variable, refusing.");
2973 /* Check for duplicates */
2975 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Duplicate '%s' record, refusing.", rec
->description
);
2977 /* Check for order */
2978 for (size_t j
= found
+ 1; j
< ELEMENTSOF(table
); j
++)
2980 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'%s' record before '%s' record, refusing.", records
[j
]->description
, rec
->description
);
2982 records
[found
] = rec
;
2985 /* Check for existence */
2986 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2987 if (table
[i
].required
&& !records
[i
])
2988 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Required record '%s' not found, refusing.", table
[i
].name
);
2990 /* At this point we know that all required variables have been measured, in the right order. */
2994 static int verb_lock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
2995 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2996 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2999 /* Lock down the EV_EFI_VARIABLE_AUTHORITY records from the existing log. Note that there's not too
3000 * much value in locking this down too much, since it stores only the result of the primary database
3001 * checks, and that's what we should bind policy to. Moreover it's hard to predict, since extension
3002 * card firmware validation will result in additional records here. */
3004 if (!is_efi_secure_boot()) {
3005 log_info("SecureBoot disabled, not generating authority .pcrlock file.");
3006 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3009 el
= event_log_new();
3013 r
= event_log_add_algorithms_from_environment(el
);
3017 r
= event_log_load(el
);
3021 r
= event_log_read_pcrs(el
);
3025 r
= event_log_calculate_pcrs(el
);
3029 /* Before we base anything on the event log records, let's check that the event log state checks
3032 r
= event_log_pcr_mask_checks_out(el
, UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY
);
3036 r
= event_log_validate_record_hashes(el
);
3040 r
= event_log_ensure_secureboot_consistency(el
);
3044 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3045 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3046 EventLogRecord
*rec
= *rr
;
3048 if (!event_log_record_is_secureboot_authority(rec
))
3051 log_debug("Locking down authority '%s'.", strna(rec
->description
));
3053 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3054 r
= json_variant_append_arrayb(
3057 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3058 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3060 return log_error_errno(r
, "Failed to build digests array: %m");
3063 r
= json_variant_append_arrayb(
3066 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3067 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3069 return log_error_errno(r
, "Failed to build record array: %m");
3072 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3075 static int verb_unlock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
3076 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3079 static int verb_lock_gpt(int argc
, char *argv
[], void *userdata
) {
3080 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *record
= NULL
;
3081 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
3082 uint8_t h
[2 * 4096]; /* space for at least two 4K sectors. GPT header should definitely be in here */
3083 uint64_t start
, n_members
, member_size
;
3084 _cleanup_close_
int fd
= -EBADF
;
3090 r
= block_device_new_from_path(
3091 argc
>= 2 ? argv
[1] : "/",
3092 BLOCK_DEVICE_LOOKUP_WHOLE_DISK
|BLOCK_DEVICE_LOOKUP_BACKING
|BLOCK_DEVICE_LOOKUP_ORIGINATING
,
3095 return log_error_errno(r
, "Failed to determine root block device: %m");
3097 fd
= sd_device_open(d
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
);
3099 return log_error_errno(fd
, "Failed to open root block device: %m");
3101 n
= pread(fd
, &h
, sizeof(h
), 0);
3103 return log_error_errno(errno
, "Failed to read GPT header of block device: %m");
3104 if ((size_t) n
!= sizeof(h
))
3105 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read trying to read GPT header: %m");
3107 /* Try a couple of sector sizes */
3108 for (size_t sz
= 512; sz
<= 4096; sz
<<= 1) {
3109 assert(sizeof(h
) >= sz
* 2);
3110 p
= (const GptHeader
*) (h
+ sz
); /* 2nd sector */
3112 if (!gpt_header_has_signature(p
))
3116 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
),
3117 "Disk has partition table for multiple sector sizes, refusing.");
3123 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3124 "Disk does not have GPT partition table, refusing.");
3126 p
= (const GptHeader
*) (h
+ found
);
3128 if (le32toh(p
->header_size
) > found
)
3129 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3130 "GPT header size over long (%" PRIu32
"), refusing.", le32toh(p
->header_size
));
3132 start
= le64toh(p
->partition_entry_lba
);
3133 if (start
> UINT64_MAX
/ found
)
3134 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3135 "Partition table start offset overflow, refusing.");
3137 member_size
= le32toh(p
->size_of_partition_entry
);
3138 if (member_size
< sizeof(GptPartitionEntry
))
3139 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3140 "Partition entry size too short, refusing.");
3142 n_members
= le32toh(p
->number_of_partition_entries
);
3143 uint64_t member_bufsz
= n_members
* member_size
;
3144 if (member_bufsz
> 1U*1024U*1024U)
3145 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3146 "Partition table size too large, refusing.");
3148 member_bufsz
= ROUND_UP(member_bufsz
, found
);
3150 _cleanup_free_
void *members
= malloc(member_bufsz
);
3154 n
= pread(fd
, members
, member_bufsz
, start
* found
);
3156 return log_error_errno(errno
, "Failed to read GPT partition table entries: %m");
3157 if ((size_t) n
!= member_bufsz
)
3158 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read while reading GPT partition table entries: %m");
3160 size_t vdata_size
= le32toh(p
->header_size
) + sizeof(le64_t
) + member_size
* n_members
;
3161 _cleanup_free_
void *vdata
= malloc0(vdata_size
);
3165 void *n_measured_entries
= mempcpy(vdata
, p
, sizeof(GptHeader
)); /* n_measured_entries is a 64bit value */
3167 void *qq
= (uint8_t*) n_measured_entries
+ sizeof(le64_t
);
3169 for (uint64_t i
= 0; i
< n_members
; i
++) {
3170 const GptPartitionEntry
*entry
= (const GptPartitionEntry
*) ((const uint8_t*) members
+ (member_size
* i
));
3172 if (memeqzero(entry
->partition_type_guid
, sizeof(entry
->partition_type_guid
)))
3175 qq
= mempcpy(qq
, entry
, member_size
);
3176 unaligned_write_le64(n_measured_entries
, unaligned_read_le64(n_measured_entries
) + 1);
3179 vdata_size
= (uint8_t*) qq
- (uint8_t*) vdata
;
3181 r
= make_pcrlock_record(TPM2_PCR_BOOT_LOADER_CONFIG
/* =5 */, vdata
, vdata_size
, &record
);
3185 r
= json_variant_new_array(&array
, &record
, 1);
3187 return log_error_errno(r
, "Failed to append to JSON array: %m");
3189 return write_pcrlock(array
, PCRLOCK_GPT_PATH
);
3192 static int verb_unlock_gpt(int argc
, char *argv
[], void *userdata
) {
3193 return unlink_pcrlock(PCRLOCK_GPT_PATH
);
3196 static bool event_log_record_is_separator(const EventLogRecord
*rec
) {
3199 /* Recognizes EV_SEPARATOR events */
3201 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3204 if (rec
->firmware_event_type
!= EV_SEPARATOR
)
3207 return rec
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
; /* Insist the record is consistent */
3210 static int event_log_record_is_action_calling_efi_app(const EventLogRecord
*rec
) {
3211 _cleanup_free_
char *d
= NULL
;
3216 /* Recognizes the special EV_EFI_ACTION that is issues when the firmware passes control to the boot loader. */
3218 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3221 if (rec
->pcr
!= TPM2_PCR_BOOT_LOADER_CODE
)
3224 if (rec
->firmware_event_type
!= EV_EFI_ACTION
)
3227 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
) /* Insist the record is consistent */
3230 r
= make_cstring(rec
->firmware_payload
, rec
->firmware_payload_size
, MAKE_CSTRING_ALLOW_TRAILING_NUL
, &d
);
3234 return streq(d
, "Calling EFI Application from Boot Option");
3237 static void enable_json_sse(void) {
3238 /* We shall write this to a single output stream? We have to output two files, hence try to be smart
3239 * and enable JSON SSE */
3241 if (!arg_pcrlock_path
&& arg_pcrlock_auto
)
3244 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_SSE
))
3247 log_notice("Enabling JSON_SEQ mode, since writing two .pcrlock files to single output.");
3248 arg_json_format_flags
|= JSON_FORMAT_SSE
;
3251 static int verb_lock_firmware(int argc
, char *argv
[], void *userdata
) {
3252 _cleanup_(json_variant_unrefp
) JsonVariant
*array_early
= NULL
, *array_late
= NULL
;
3253 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
3254 uint32_t always_mask
, separator_mask
, separator_seen_mask
= 0, action_seen_mask
= 0;
3255 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3260 /* The PCRs we intend to cover. Note that we measure firmware, external *and* boot loader code/config
3261 * here – but the latter only until the "separator" events are seen, which tell us where transition
3262 * into OS boot loader happens. This reflects the fact that on some systems the firmware already
3263 * measures some firmware-supplied apps into PCR 4. (e.g. Thinkpad X1 Gen9) */
3264 if (endswith(argv
[0], "firmware-code")) {
3265 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CODE
) | /* → 0 */
3266 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE
); /* → 2 */
3268 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
; /* → 4 */
3270 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3271 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3273 assert(endswith(argv
[0], "firmware-config"));
3274 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG
) | /* → 1 */
3275 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG
); /* → 3 */
3277 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG
; /* → 5 */
3279 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3280 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3283 el
= event_log_new();
3287 r
= event_log_add_algorithms_from_environment(el
);
3291 r
= event_log_load(el
);
3295 r
= event_log_read_pcrs(el
);
3299 r
= event_log_calculate_pcrs(el
);
3303 r
= event_log_validate_record_hashes(el
);
3307 /* Before we base anything on the event log records for any of the selected PCRs, let's check that
3308 * the event log state checks out for them. */
3310 r
= event_log_pcr_mask_checks_out(el
, always_mask
|separator_mask
);
3314 // FIXME: before doing this, validate ahead-of-time that EV_SEPARATOR records exist for all entries,
3317 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3318 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3319 EventLogRecord
*rec
= *rr
;
3320 uint32_t bit
= UINT32_C(1) << rec
->pcr
;
3322 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3325 if (!FLAGS_SET(always_mask
, bit
) &&
3326 !(FLAGS_SET(separator_mask
, bit
) && !FLAGS_SET(separator_seen_mask
|action_seen_mask
, bit
)))
3329 /* If we hit the separator record, we stop processing the PCRs listed in `separator_mask` */
3330 if (event_log_record_is_separator(rec
)) {
3331 separator_seen_mask
|= bit
;
3335 /* If we hit the special "Calling EFI Application from Boot Option" action we treat this the
3336 * same as a separator here, as that's where firmware passes control to boot loader. Note
3337 * that some EFI implementations forget to generate one of them. */
3338 r
= event_log_record_is_action_calling_efi_app(rec
);
3340 return log_error_errno(r
, "Failed to check if event is 'Calling EFI Application from Boot Option' action: %m");
3342 action_seen_mask
|= bit
;
3346 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3347 r
= json_variant_append_arrayb(
3350 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3351 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3353 return log_error_errno(r
, "Failed to build digests array: %m");
3356 r
= json_variant_append_arrayb(
3357 FLAGS_SET(separator_seen_mask
, bit
) ? &array_late
: &array_early
,
3359 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3360 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3362 return log_error_errno(r
, "Failed to build record array: %m");
3365 r
= write_pcrlock(array_early
, default_pcrlock_early_path
);
3369 return write_pcrlock(array_late
, default_pcrlock_late_path
);
3372 static int verb_unlock_firmware(int argc
, char *argv
[], void *userdata
) {
3373 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3376 if (endswith(argv
[0], "firmware-code")) {
3377 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3378 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3380 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3381 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3384 r
= unlink_pcrlock(default_pcrlock_early_path
);
3388 if (arg_pcrlock_path
) /* if the path is specified don't delete the same thing twice */
3391 r
= unlink_pcrlock(default_pcrlock_late_path
);
3398 static int verb_lock_machine_id(int argc
, char *argv
[], void *userdata
) {
3399 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3400 _cleanup_free_
char *word
= NULL
;
3403 r
= pcrextend_machine_id_word(&word
);
3407 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3411 r
= json_variant_new_array(&array
, &record
, 1);
3413 return log_error_errno(r
, "Failed to create record array: %m");
3415 return write_pcrlock(array
, PCRLOCK_MACHINE_ID_PATH
);
3418 static int verb_unlock_machine_id(int argc
, char *argv
[], void *userdata
) {
3419 return unlink_pcrlock(PCRLOCK_MACHINE_ID_PATH
);
3422 static int pcrlock_file_system_path(const char *normalized_path
, char **ret
) {
3423 _cleanup_free_
char *s
= NULL
;
3425 assert(normalized_path
);
3427 if (path_equal(normalized_path
, "/"))
3428 s
= strdup(PCRLOCK_ROOT_FILE_SYSTEM_PATH
);
3430 /* We reuse the escaping we use for turning paths into unit names */
3431 _cleanup_free_
char *escaped
= NULL
;
3433 assert(normalized_path
[0] == '/');
3434 assert(normalized_path
[1] != '/');
3436 escaped
= unit_name_escape(normalized_path
+ 1);
3440 s
= strjoin(PCRLOCK_FILE_SYSTEM_PATH_PREFIX
, escaped
, ".pcrlock");
3449 static int verb_lock_file_system(int argc
, char *argv
[], void *userdata
) {
3450 const char* paths
[3] = {};
3459 r
= get_block_device("/", &a
);
3461 return log_error_errno(r
, "Failed to get device of root file system: %m");
3463 r
= get_block_device("/var", &b
);
3465 return log_error_errno(r
, "Failed to get device of /var/ file system: %m");
3467 /* if backing device is distinct, then measure /var/ too */
3474 STRV_FOREACH(p
, paths
) {
3475 _cleanup_free_
char *word
= NULL
, *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3476 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3478 r
= pcrextend_file_system_word(*p
, &word
, &normalized_path
);
3482 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3486 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3490 r
= json_variant_new_array(&array
, &record
, 1);
3492 return log_error_errno(r
, "Failed to create record array: %m");
3494 r
= write_pcrlock(array
, pcrlock_file
);
3502 static int verb_unlock_file_system(int argc
, char *argv
[], void *userdata
) {
3503 const char* paths
[3] = {};
3513 STRV_FOREACH(p
, paths
) {
3514 _cleanup_free_
char *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3516 r
= chase(*p
, NULL
, 0, &normalized_path
, NULL
);
3518 return log_error_errno(r
, "Failed to normal path '%s': %m", argv
[1]);
3520 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3524 r
= unlink_pcrlock(pcrlock_file
);
3532 static int verb_lock_pe(int argc
, char *argv
[], void *userdata
) {
3533 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
3534 _cleanup_close_
int fd
= -EBADF
;
3537 // FIXME: Maybe also generate a matching EV_EFI_VARIABLE_AUTHORITY records here for each signature that
3538 // covers this PE plus its hash, as alternatives under the same component name
3541 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3543 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3546 if (arg_pcr_mask
== 0)
3547 arg_pcr_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
;
3549 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
3550 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3552 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
3555 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
3556 _cleanup_free_
void *hash
= NULL
;
3561 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
3562 assert_se(md
= EVP_get_digestbyname(a
));
3564 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &hash
, &hash_size
);
3566 return log_error_errno(r
, "Failed to hash PE binary: %m");
3568 r
= json_variant_append_arrayb(&digests
,
3570 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3571 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_size
))));
3573 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3576 r
= json_variant_append_arrayb(
3579 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i
)),
3580 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3582 return log_error_errno(r
, "Failed to append record object: %m");
3585 return write_pcrlock(array
, NULL
);
3588 typedef void* SectionHashArray
[_UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
];
3590 static void section_hashes_array_done(SectionHashArray
*array
) {
3593 for (size_t i
= 0; i
< _UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
; i
++)
3597 static int verb_lock_uki(int argc
, char *argv
[], void *userdata
) {
3598 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *pe_digests
= NULL
;
3599 _cleanup_(section_hashes_array_done
) SectionHashArray section_hashes
= {};
3600 size_t hash_sizes
[TPM2_N_HASH_ALGORITHMS
];
3601 _cleanup_close_
int fd
= -EBADF
;
3604 if (arg_pcr_mask
!= 0)
3605 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "PCR not configurable for UKI lock down.");
3608 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3610 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3613 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3614 _cleanup_free_
void *peh
= NULL
;
3618 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3619 assert_se(md
= EVP_get_digestbyname(a
));
3621 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &peh
, hash_sizes
+ i
);
3623 return log_error_errno(r
, "Failed to hash PE binary: %m");
3625 r
= json_variant_append_arrayb(
3628 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3629 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(peh
, hash_sizes
[i
]))));
3631 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3633 r
= uki_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, section_hashes
+ (i
* _UNIFIED_SECTION_MAX
), hash_sizes
+ i
);
3635 return log_error_errno(r
, "Failed to UKI hash PE binary: %m");
3638 r
= json_variant_append_arrayb(
3641 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_BOOT_LOADER_CODE
)),
3642 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(pe_digests
))));
3644 return log_error_errno(r
, "Failed to append record object: %m");
3646 for (UnifiedSection section
= 0; section
< _UNIFIED_SECTION_MAX
; section
++) {
3647 _cleanup_(json_variant_unrefp
) JsonVariant
*section_digests
= NULL
, *record
= NULL
;
3649 if (!unified_section_measure(section
))
3652 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3656 hash
= section_hashes
[i
* _UNIFIED_SECTION_MAX
+ section
];
3660 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3662 r
= json_variant_append_arrayb(
3665 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3666 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_sizes
[i
]))));
3668 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3671 if (!section_digests
)
3674 /* So we have digests for this section, hence generate a record for the section name first. */
3675 r
= make_pcrlock_record(TPM2_PCR_KERNEL_BOOT
/* =11 */, unified_sections
[section
], strlen(unified_sections
[section
]) + 1, &record
);
3679 r
= json_variant_append_array(&array
, record
);
3681 return log_error_errno(r
, "Failed to append JSON record array: %m");
3683 /* And then append a record for the section contents digests as well */
3684 r
= json_variant_append_arrayb(
3687 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_KERNEL_BOOT
/* =11 */)),
3688 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(section_digests
))));
3690 return log_error_errno(r
, "Failed to append record object: %m");
3693 return write_pcrlock(array
, NULL
);
3696 static int event_log_reduce_to_safe_pcrs(EventLog
*el
, uint32_t *pcrs
) {
3697 _cleanup_free_
char *dropped
= NULL
, *kept
= NULL
;
3702 /* When we compile a new PCR policy we don't want to bind to PCRs which are fishy for one of three
3705 * 1. The PCR value doesn't match the event log
3706 * 2. The event log for the PCR contains measurements we don't know responsible components for
3707 * 3. The event log for the PCR does not contain measurements for components we know
3709 * This function checks for the three conditions and drops the PCR from the mask.
3712 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
3714 if (!FLAGS_SET(*pcrs
, UINT32_C(1) << pcr
))
3717 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
)) {
3718 log_notice("PCR %" PRIu32
" (%s) value does not match event log. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3722 if (!el
->registers
[pcr
].fully_recognized
) {
3723 log_notice("PCR %" PRIu32
" (%s) event log contains unrecognized measurements. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3727 if (FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
)) {
3728 log_notice("PCR %" PRIu32
" (%s) is touched by component we can't find in event log. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3732 log_info("PCR %" PRIu32
" (%s) matches event log and fully consists of recognized measurements. Including in set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3734 if (strextendf_with_separator(&kept
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3740 *pcrs
&= ~(UINT32_C(1) << pcr
);
3742 if (strextendf_with_separator(&dropped
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3747 log_notice("PCRs dropped from protection mask: %s", dropped
);
3749 log_debug("No PCRs dropped from protection mask.");
3752 log_notice("PCRs in protection mask: %s", kept
);
3754 log_notice("No PCRs kept in protection mask.");
3759 static int verb_lock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3760 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3761 _cleanup_free_
char *cmdline
= NULL
;
3765 if (empty_or_dash(argv
[1]))
3766 r
= read_full_stream(stdin
, &cmdline
, NULL
);
3768 r
= read_full_file(argv
[1], &cmdline
, NULL
);
3770 r
= proc_cmdline(&cmdline
);
3772 return log_error_errno(r
, "Failed to read cmdline: %m");
3774 delete_trailing_chars(cmdline
, "\n");
3776 _cleanup_free_ char16_t
*u
= NULL
;
3777 u
= utf8_to_utf16(cmdline
, SIZE_MAX
);
3781 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, u
, char16_strlen(u
)*2+2, &record
);
3785 r
= json_variant_new_array(&array
, &record
, 1);
3787 return log_error_errno(r
, "Failed to create record array: %m");
3789 r
= write_pcrlock(array
, PCRLOCK_KERNEL_CMDLINE_PATH
);
3796 static int verb_unlock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3797 return unlink_pcrlock(PCRLOCK_KERNEL_CMDLINE_PATH
);
3800 static int verb_lock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3801 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3802 _cleanup_free_
void *data
= NULL
;
3803 _cleanup_fclose_
FILE *f
= NULL
;
3808 f
= fopen(argv
[1], "re");
3810 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3813 r
= read_full_stream(f
?: stdin
, (char**) &data
, &size
);
3815 return log_error_errno(r
, "Failed to read data from stdin: %m");
3817 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, data
, size
, &record
);
3821 r
= json_variant_new_array(&array
, &record
, 1);
3823 return log_error_errno(r
, "Failed to create record array: %m");
3825 r
= write_pcrlock(array
, PCRLOCK_KERNEL_INITRD_PATH
);
3832 static int verb_unlock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3833 return unlink_pcrlock(PCRLOCK_KERNEL_INITRD_PATH
);
3836 static int pcr_prediction_add_result(
3837 Tpm2PCRPrediction
*context
,
3838 Tpm2PCRPredictionResult
*result
,
3843 _cleanup_free_ Tpm2PCRPredictionResult
*copy
= NULL
;
3849 copy
= newdup(Tpm2PCRPredictionResult
, result
, 1);
3853 r
= ordered_set_ensure_put(context
->results
+ pcr
, &tpm2_pcr_prediction_result_hash_ops
, copy
);
3854 if (r
== -EEXIST
) /* Multiple identical results for the same PCR are totally expected */
3857 return log_error_errno(r
, "Failed to insert result into set: %m");
3859 log_debug("Added prediction result %u for PCR %" PRIu32
" (path: %s)", ordered_set_size(context
->results
[pcr
]), pcr
, strempty(path
));
3865 static const EVP_MD
* evp_from_tpm2_alg(uint16_t alg
) {
3868 name
= tpm2_hash_alg_to_string(alg
);
3872 return EVP_get_digestbyname(name
);
3875 static int event_log_component_variant_calculate(
3876 Tpm2PCRPrediction
*context
,
3877 Tpm2PCRPredictionResult
*result
,
3878 EventLogComponent
*component
,
3879 EventLogComponentVariant
*variant
,
3890 FOREACH_ARRAY(rr
, variant
->records
, variant
->n_records
) {
3891 EventLogRecord
*rec
= *rr
;
3893 if (rec
->pcr
!= pcr
)
3896 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3897 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*md_ctx
= NULL
;
3898 EventLogRecordBank
*b
;
3900 if (result
->hash
[i
].size
<= 0) /* already invalidated */
3903 b
= event_log_record_find_bank(rec
, tpm2_hash_algorithms
[i
]);
3905 /* Can't calculate, hence invalidate */
3906 result
->hash
[i
] = (TPM2B_DIGEST
) {};
3910 md_ctx
= EVP_MD_CTX_new();
3914 const EVP_MD
*md
= ASSERT_PTR(evp_from_tpm2_alg(tpm2_hash_algorithms
[i
]));
3916 int sz
= EVP_MD_size(md
);
3918 assert((size_t) sz
<= sizeof_field(TPM2B_DIGEST
, buffer
));
3920 assert(sz
== tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]));
3922 assert(result
->hash
[i
].size
== (size_t) sz
);
3923 assert(b
->hash
.size
== (size_t) sz
);
3925 if (EVP_DigestInit_ex(md_ctx
, md
, NULL
) != 1)
3926 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to initialize message digest.");
3928 if (EVP_DigestUpdate(md_ctx
, result
->hash
[i
].buffer
, sz
) != 1)
3929 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash bank value.");
3931 if (EVP_DigestUpdate(md_ctx
, b
->hash
.buffer
, sz
) != 1)
3932 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data value.");
3934 unsigned l
= (unsigned) sz
;
3935 if (EVP_DigestFinal_ex(md_ctx
, result
->hash
[i
].buffer
, &l
) != 1)
3936 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to finalize message digest.");
3938 assert(l
== (unsigned) sz
);
3941 /* This is a valid result once we hit the start location */
3942 if (arg_location_start
&& strcmp(component
->id
, arg_location_start
) >= 0) {
3943 r
= pcr_prediction_add_result(context
, result
, pcr
, path
, rr
- variant
->records
);
3952 static int event_log_predict_pcrs(
3954 Tpm2PCRPrediction
*context
,
3955 Tpm2PCRPredictionResult
*parent_result
,
3956 size_t component_index
,
3960 EventLogComponent
*component
;
3965 assert(parent_result
);
3967 /* Check if we reached the end of the components, generate a result, and backtrack */
3968 if (component_index
>= el
->n_components
||
3969 (arg_location_end
&& strcmp(el
->components
[component_index
]->id
, arg_location_end
) > 0)) {
3970 r
= pcr_prediction_add_result(context
, parent_result
, pcr
, path
, /* offset= */ 0);
3977 component
= ASSERT_PTR(el
->components
[component_index
]);
3979 if (component
->n_variants
== 0)
3980 return event_log_predict_pcrs(
3984 component_index
+ 1, /* Next component */
3988 FOREACH_ARRAY(ii
, component
->variants
, component
->n_variants
) {
3989 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
3990 EventLogComponentVariant
*variant
= *ii
;
3991 _cleanup_free_
char *subpath
= NULL
;
3993 /* Operate on a copy of the result */
3996 subpath
= strjoin(path
, ":", component
->id
);
3998 subpath
= strdup(component
->id
);
4002 if (!streq(component
->id
, variant
->id
))
4003 if (!strextend(&subpath
, "@", variant
->id
))
4006 result
= newdup(Tpm2PCRPredictionResult
, parent_result
, 1);
4010 r
= event_log_component_variant_calculate(
4020 r
= event_log_predict_pcrs(
4024 component_index
+ 1, /* Next component */
4036 static ssize_t
event_log_calculate_component_combinations(EventLog
*el
) {
4040 FOREACH_ARRAY(cc
, el
->components
, el
->n_components
) {
4041 EventLogComponent
*c
= *cc
;
4043 /* Overflow check */
4044 if (c
->n_variants
> (size_t) (SSIZE_MAX
/count
))
4045 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Too many component combinations.");
4046 /* If no variant, this will lead to count being 0 and sigfpe */
4047 if (c
->n_variants
== 0)
4049 count
*= c
->n_variants
;
4055 static int event_log_show_predictions(Tpm2PCRPrediction
*context
, uint16_t alg
) {
4060 pager_open(arg_pager_flags
);
4062 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
4063 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
4065 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
4066 _cleanup_(json_variant_unrefp
) JsonVariant
*aj
= NULL
;
4068 r
= tpm2_pcr_prediction_to_json(
4070 tpm2_hash_algorithms
[i
],
4075 if (json_variant_elements(aj
) == 0)
4078 r
= json_variant_set_field(
4080 tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]),
4083 return log_error_errno(r
, "Failed to add prediction bank to object: %m");
4087 r
= json_variant_new_object(&j
, NULL
, 0);
4089 return log_error_errno(r
, "Failed to allocated empty object: %m");
4092 json_variant_dump(j
, arg_json_format_flags
, /* f= */ NULL
, /* prefix= */ NULL
);
4096 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4097 Tpm2PCRPredictionResult
*result
;
4098 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4101 if (ordered_set_isempty(context
->results
[pcr
])) {
4102 printf("No results for PCR %u (%s).\n", pcr
, tpm2_pcr_index_to_string(pcr
));
4106 printf("%sResults for PCR %u (%s):%s\n", ansi_underline(), pcr
, tpm2_pcr_index_to_string(pcr
), ansi_normal());
4108 ORDERED_SET_FOREACH(result
, context
->results
[pcr
]) {
4110 _cleanup_free_
char *aa
= NULL
, *h
= NULL
;
4113 TPM2B_DIGEST
*hash
= tpm2_pcr_prediction_result_get_hash(result
, alg
);
4117 a
= ASSERT_PTR(tpm2_hash_alg_to_string(alg
));
4124 h
= hexmem(hash
->buffer
, hash
->size
);
4128 printf(" %s%-6s:%s %s\n", ansi_grey(), aa
, ansi_normal(), h
);
4135 static int tpm2_pcr_prediction_run(
4137 Tpm2PCRPrediction
*context
) {
4144 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4145 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
4147 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4150 result
= new0(Tpm2PCRPredictionResult
, 1);
4154 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++)
4155 event_log_initial_pcr_state(el
, pcr
, tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]), result
->hash
+ i
);
4157 r
= event_log_predict_pcrs(
4161 /* component_index= */ 0,
4171 static int verb_predict(int argc
, char *argv
[], void *userdata
) {
4172 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction context
= {
4173 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4175 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4179 r
= event_log_load_and_process(&el
);
4183 count
= event_log_calculate_component_combinations(el
);
4187 log_info("%zi combinations of components.", count
);
4189 r
= event_log_reduce_to_safe_pcrs(el
, &context
.pcrs
);
4193 r
= tpm2_pcr_prediction_run(el
, &context
);
4197 return event_log_show_predictions(&context
, el
->primary_algorithm
);
4200 static int remove_policy_file(const char *path
) {
4203 if (unlink(path
) < 0) {
4204 if (errno
== ENOENT
)
4207 return log_error_errno(errno
, "Failed to remove policy file '%s': %m", path
);
4210 log_info("Removed policy file '%s'.", path
);
4214 static int verb_make_policy(int argc
, char *argv
[], void *userdata
) {
4217 /* Here's how this all works: after predicting all possible PCR values for next boot (with
4218 * alternatives) we'll calculate a policy from it as a combination of PolicyPCR + PolicyOR
4219 * expressions. This is then stored in an NV index. When a component of the boot process is changed a
4220 * new prediction is made and the NV index updated (which automatically invalidates any older
4223 * Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
4224 * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
4225 * policies matching the current NV index contents.
4227 * We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
4228 * either generate locally or which the user can provide us with) which can also be used for
4229 * recovery. This PIN is sealed to the TPM and is locked via PolicyAuthorizeNV to the NV index it
4230 * protects (i.e. we dogfood 🌭 🐶 hard here). This means in order to update such a policy we need
4231 * the policy to pass.
4233 * Information about the used NV Index, the SRK of the TPM, the sealed PIN and the current PCR
4234 * prediction data are stored in a JSON file in /var/lib/. In order to be able to unlock root disks
4235 * this data must be also copied to the ESP so that it is available to the initrd. The data is not
4236 * sensitive, as SRK and NV index are pinned by it, and the prediction data must match the NV index
4239 usec_t start_usec
= now(CLOCK_MONOTONIC
);
4241 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4242 r
= event_log_load_and_process(&el
);
4246 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction new_prediction
= {
4247 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4249 r
= event_log_reduce_to_safe_pcrs(el
, &new_prediction
.pcrs
);
4253 usec_t predict_start_usec
= now(CLOCK_MONOTONIC
);
4255 r
= tpm2_pcr_prediction_run(el
, &new_prediction
);
4259 log_info("Predicted future PCRs in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), predict_start_usec
), 1));
4261 _cleanup_(json_variant_unrefp
) JsonVariant
*new_prediction_json
= NULL
;
4262 r
= tpm2_pcr_prediction_to_json(&new_prediction
, el
->primary_algorithm
, &new_prediction_json
);
4267 (void) json_variant_dump(new_prediction_json
, JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
, stderr
, NULL
);
4269 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy old_policy
= {};
4271 r
= tpm2_pcrlock_policy_load(arg_pcrlock_path
, &old_policy
);
4275 bool have_old_policy
= r
> 0;
4277 /* When we update the policy the old serializations for NV, SRK, PIN remain the same */
4278 _cleanup_(iovec_done
) struct iovec
4279 nv_blob
= TAKE_STRUCT(old_policy
.nv_handle
),
4280 nv_public_blob
= TAKE_STRUCT(old_policy
.nv_public
),
4281 srk_blob
= TAKE_STRUCT(old_policy
.srk_handle
),
4282 pin_public
= TAKE_STRUCT(old_policy
.pin_public
),
4283 pin_private
= TAKE_STRUCT(old_policy
.pin_private
);
4285 if (have_old_policy
) {
4286 if (arg_nv_index
!= 0 && old_policy
.nv_index
!= arg_nv_index
)
4287 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Stored policy references different NV index (0x%x) than specified (0x%x), refusing.", old_policy
.nv_index
, arg_nv_index
);
4290 old_policy
.algorithm
== el
->primary_algorithm
&&
4291 tpm2_pcr_prediction_equal(&old_policy
.prediction
, &new_prediction
, el
->primary_algorithm
)) {
4292 log_info("Prediction is identical to current policy, skipping update.");
4293 return EXIT_SUCCESS
;
4297 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4298 r
= tpm2_context_new_or_warn(/* device= */ NULL
, &tc
);
4302 if (!tpm2_supports_command(tc
, TPM2_CC_PolicyAuthorizeNV
))
4303 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "TPM2 does not support PolicyAuthorizeNV command, refusing.");
4305 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4307 if (iovec_is_set(&srk_blob
)) {
4308 r
= tpm2_deserialize(
4314 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4316 r
= tpm2_get_or_create_srk(
4318 /* session= */ NULL
,
4319 /* ret_public= */ NULL
,
4320 /* ret_name= */ NULL
,
4321 /* ret_qname= */ NULL
,
4324 return log_error_errno(r
, "Failed to install SRK: %m");
4327 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4328 r
= tpm2_make_encryption_session(
4331 /* bind_key= */ &TPM2_HANDLE_NONE
,
4332 &encryption_session
);
4334 return log_error_errno(r
, "Failed to allocate encryption session: %m");
4336 /* Acquire a recovery PIN, either from the user, or create a randomized one */
4337 _cleanup_(erase_and_freep
) char *pin
= NULL
;
4338 if (arg_recovery_pin
) {
4339 r
= getenv_steal_erase("PIN", &pin
);
4341 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
4343 _cleanup_(strv_free_erasep
) char **l
= NULL
;
4345 r
= ask_password_auto(
4348 /* id= */ "pcrlock-recovery-pin",
4349 /* key_name= */ NULL
,
4350 /* credential_name= */ "systemd-pcrlock.recovery-pin",
4355 return log_error_errno(r
, "Failed to query for recovery PIN: %m");
4357 if (strv_length(l
) != 1)
4358 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expected a single PIN only.");
4360 pin
= TAKE_PTR(l
[0]);
4364 } else if (!have_old_policy
) {
4367 r
= crypto_random_bytes(rnd
, sizeof(rnd
));
4369 return log_error_errno(r
, "Failed to generate a randomized recovery PIN: %m");
4371 (void) base64mem(rnd
, sizeof(rnd
), &pin
);
4372 explicit_bzero_safe(rnd
, sizeof(rnd
));
4377 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4378 TPM2_HANDLE nv_index
= 0;
4380 if (iovec_is_set(&nv_blob
)) {
4381 r
= tpm2_deserialize(tc
, nv_blob
.iov_base
, nv_blob
.iov_len
, &nv_handle
);
4383 return log_error_errno(r
, "Failed to deserialize NV index TR: %m");
4385 nv_index
= old_policy
.nv_index
;
4388 TPM2B_AUTH auth
= {};
4389 CLEANUP_ERASE(auth
);
4392 r
= tpm2_get_pin_auth(TPM2_ALG_SHA256
, pin
, &auth
);
4394 return log_error_errno(r
, "Failed to hash PIN: %m");
4396 assert(iovec_is_set(&pin_public
));
4397 assert(iovec_is_set(&pin_private
));
4399 log_debug("Retrieving PIN from sealed data.");
4401 usec_t pin_start_usec
= now(CLOCK_MONOTONIC
);
4403 _cleanup_(iovec_done_erase
) struct iovec secret
= {};
4404 for (unsigned attempt
= 0;; attempt
++) {
4405 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4407 r
= tpm2_make_policy_session(
4413 return log_error_errno(r
, "Failed to allocate policy session: %m");
4415 r
= tpm2_policy_super_pcr(
4418 &old_policy
.prediction
,
4419 old_policy
.algorithm
);
4421 return log_error_errno(r
, "Failed to submit super PCR policy: %m");
4423 r
= tpm2_policy_authorize_nv(
4429 return log_error_errno(r
, "Failed to submit AuthorizeNV policy: %m");
4431 r
= tpm2_unseal_data(
4439 if (r
< 0 && (r
!= -ESTALE
|| attempt
>= 16))
4440 return log_error_errno(r
, "Failed to unseal PIN: %m");
4444 log_debug("Trying again (attempt %u), as PCR values changed during unlock attempt.", attempt
+1);
4447 if (secret
.iov_len
> sizeof_field(TPM2B_AUTH
, buffer
))
4448 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Decrypted PIN too large.");
4450 auth
= (TPM2B_AUTH
) {
4451 .size
= secret
.iov_len
,
4454 memcpy_safe(auth
.buffer
, secret
.iov_base
, secret
.iov_len
);
4456 log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_start_usec
), 1));
4459 TPM2B_NV_PUBLIC nv_public
= {};
4461 usec_t nv_index_start_usec
= now(CLOCK_MONOTONIC
);
4463 if (!iovec_is_set(&nv_blob
)) {
4464 TPM2B_DIGEST recovery_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4465 r
= tpm2_calculate_policy_auth_value(&recovery_policy_digest
);
4467 return log_error_errno(r
, "Failed to calculate authentication value policy: %m");
4469 log_debug("Allocating NV index to write PCR policy to...");
4470 r
= tpm2_define_policy_nv_index(
4474 &recovery_policy_digest
,
4481 return log_error_errno(r
, "NV index 0x%" PRIx32
" already allocated.", arg_nv_index
);
4483 return log_error_errno(r
, "Failed to allocate NV index: %m");
4486 r
= tpm2_set_auth_binary(tc
, nv_handle
, &auth
);
4488 return log_error_errno(r
, "Failed to set authentication value on NV index: %m");
4490 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4491 r
= tpm2_make_policy_session(
4497 return log_error_errno(r
, "Failed to allocate policy session: %m");
4499 r
= tpm2_policy_auth_value(
4502 /* ret_policy_digest= */ NULL
);
4504 return log_error_errno(r
, "Failed to submit authentication value policy: %m");
4506 log_debug("Calculating new PCR policy to write...");
4507 TPM2B_DIGEST new_super_pcr_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4509 usec_t pcr_policy_start_usec
= now(CLOCK_MONOTONIC
);
4511 r
= tpm2_calculate_policy_super_pcr(
4513 el
->primary_algorithm
,
4514 &new_super_pcr_policy_digest
);
4516 return log_error_errno(r
, "Failed to calculate super PCR policy: %m");
4518 log_info("Calculated new PCR policy in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pcr_policy_start_usec
), 1));
4520 log_debug("Writing new PCR policy to NV index...");
4521 r
= tpm2_write_policy_nv_index(
4526 &new_super_pcr_policy_digest
);
4528 return log_error_errno(r
, "Failed to write to NV index: %m");
4530 log_info("Updated NV index in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), nv_index_start_usec
), 1));
4532 assert(iovec_is_set(&pin_public
) == iovec_is_set(&pin_private
));
4533 if (!iovec_is_set(&pin_public
)) {
4534 TPM2B_DIGEST authnv_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4536 r
= tpm2_calculate_policy_authorize_nv(&nv_public
, &authnv_policy_digest
);
4538 return log_error_errno(r
, "Failed to calculate AuthorizeNV policy: %m");
4540 struct iovec data
= {
4541 .iov_base
= auth
.buffer
,
4542 .iov_len
= auth
.size
,
4545 usec_t pin_seal_start_usec
= now(CLOCK_MONOTONIC
);
4547 log_debug("Sealing PIN to NV index policy...");
4553 &authnv_policy_digest
,
4557 return log_error_errno(r
, "Failed to seal PIN to NV auth policy: %m");
4559 log_info("Sealed PIN in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_seal_start_usec
), 1));
4562 if (!iovec_is_set(&nv_blob
)) {
4563 r
= tpm2_serialize(tc
, nv_handle
, &nv_blob
.iov_base
, &nv_blob
.iov_len
);
4565 return log_error_errno(r
, "Failed to serialize NV index TR: %m");
4568 if (!iovec_is_set(&srk_blob
)) {
4569 r
= tpm2_serialize(tc
, srk_handle
, &srk_blob
.iov_base
, &srk_blob
.iov_len
);
4571 return log_error_errno(r
, "Failed to serialize SRK index TR: %m");
4574 if (!iovec_is_set(&nv_public_blob
)) {
4575 r
= tpm2_marshal_nv_public(&nv_public
, &nv_public_blob
.iov_base
, &nv_public_blob
.iov_len
);
4577 return log_error_errno(r
, "Failed to marshal NV public area: %m");
4580 _cleanup_(json_variant_unrefp
) JsonVariant
*new_configuration_json
= NULL
;
4581 r
= json_build(&new_configuration_json
,
4583 JSON_BUILD_PAIR_STRING("pcrBank", tpm2_hash_alg_to_string(el
->primary_algorithm
)),
4584 JSON_BUILD_PAIR_VARIANT("pcrValues", new_prediction_json
),
4585 JSON_BUILD_PAIR_INTEGER("nvIndex", nv_index
),
4586 JSON_BUILD_PAIR_IOVEC_BASE64("nvHandle", &nv_blob
),
4587 JSON_BUILD_PAIR_IOVEC_BASE64("nvPublic", &nv_public_blob
),
4588 JSON_BUILD_PAIR_IOVEC_BASE64("srkHandle", &srk_blob
),
4589 JSON_BUILD_PAIR_IOVEC_BASE64("pinPublic", &pin_public
),
4590 JSON_BUILD_PAIR_IOVEC_BASE64("pinPrivate", &pin_private
)));
4592 return log_error_errno(r
, "Failed to generate JSON: %m");
4594 _cleanup_free_
char *text
= NULL
;
4595 r
= json_variant_format(new_configuration_json
, 0, &text
);
4597 return log_error_errno(r
, "Failed to format new configuration to JSON: %m");
4599 const char *path
= arg_pcrlock_path
?: (in_initrd() ? "/run/systemd/pcrlock.json" : "/var/lib/systemd/pcrlock.json");
4600 r
= write_string_file(path
, text
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_SYNC
|WRITE_STRING_FILE_MKDIR_0755
);
4602 return log_error_errno(r
, "Failed to write new configuration to '%s': %m", path
);
4604 if (!arg_pcrlock_path
&& !in_initrd()) {
4605 r
= remove_policy_file("/run/systemd/pcrlock.json");
4610 log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%x.", path
, nv_index
);
4612 log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), start_usec
), 1));
4617 static int undefine_policy_nv_index(
4619 const struct iovec
*nv_blob
,
4620 const struct iovec
*srk_blob
) {
4626 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4627 r
= tpm2_context_new_or_warn(/* device= */ NULL
, &tc
);
4631 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4632 r
= tpm2_deserialize(
4638 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4640 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4641 r
= tpm2_deserialize(
4647 return log_error_errno(r
, "Failed to deserialize NV TR: %m");
4649 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4650 r
= tpm2_make_encryption_session(
4653 /* bind_key= */ &TPM2_HANDLE_NONE
,
4654 &encryption_session
);
4658 r
= tpm2_undefine_policy_nv_index(
4666 log_info("Removed NV index 0x%x", nv_index
);
4670 static int verb_remove_policy(int argc
, char *argv
[], void *userdata
) {
4673 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy policy
= {};
4674 r
= tpm2_pcrlock_policy_load(arg_policy_path
, &policy
);
4676 log_info("No policy found.");
4681 log_notice("Failed to load old policy file, assuming it is corrupted, removing.");
4683 r
= undefine_policy_nv_index(policy
.nv_index
, &policy
.nv_handle
, &policy
.srk_handle
);
4685 log_notice("Failed to remove NV index, assuming data out of date, removing policy file.");
4688 if (arg_policy_path
) {
4689 r
= remove_policy_file(arg_policy_path
);
4697 RET_GATHER(ret
, remove_policy_file("/var/lib/systemd/pcrlock.json"));
4698 RET_GATHER(ret
, remove_policy_file("/run/systemd/pcrlock.json"));
4704 static int help(int argc
, char *argv
[], void *userdata
) {
4705 _cleanup_free_
char *link
= NULL
;
4708 r
= terminal_urlify_man("systemd-pcrlock", "8", &link
);
4712 printf("%1$s [OPTIONS...] COMMAND ...\n"
4713 "\n%5$sManage a TPM2 PCR lock.%6$s\n"
4714 "\n%3$sCommands:%4$s\n"
4715 " log Show measurement log\n"
4716 " cel Show measurement log in TCG CEL-JSON format\n"
4717 " list-components List defined .pcrlock components\n"
4718 " predict Predict PCR values\n"
4719 " make-policy Predict PCR values and generate TPM2 policy from it\n"
4720 " remove-policy Remove TPM2 policy\n"
4721 "\n%3$sProtections:%4$s\n"
4722 " lock-firmware-code Generate a .pcrlock file from current firmware code\n"
4723 " unlock-firmware-code Remove .pcrlock file for firmware code\n"
4724 " lock-firmware-config Generate a .pcrlock file from current firmware configuration\n"
4725 " unlock-firmware-config Remove .pcrlock file for firmware configuration\n"
4726 " lock-secureboot-policy Generate a .pcrlock file from current SecureBoot policy\n"
4727 " unlock-secureboot-policy Remove .pcrlock file for SecureBoot policy\n"
4728 " lock-secureboot-authority Generate a .pcrlock file from current SecureBoot authority\n"
4729 " unlock-secureboot-authority Remove .pcrlock file for SecureBoot authority\n"
4730 " lock-gpt [DISK] Generate a .pcrlock file from GPT header\n"
4731 " unlock-gpt Remove .pcrlock file for GPT header\n"
4732 " lock-pe [BINARY] Generate a .pcrlock file from PE binary\n"
4733 " unlock-pe Remove .pcrlock file for PE binary\n"
4734 " lock-uki [UKI] Generate a .pcrlock file from UKI PE binary\n"
4735 " unlock-uki Remove .pcrlock file for UKI PE binary\n"
4736 " lock-machine-id Generate a .pcrlock file from current machine ID\n"
4737 " unlock-machine-id Remove .pcrlock file for machine ID\n"
4738 " lock-file-system [PATH] Generate a .pcrlock file from current root fs + /var/\n"
4739 " unlock-file-system [PATH] Remove .pcrlock file for root fs + /var/\n"
4740 " lock-kernel-cmdline [FILE] Generate a .pcrlock file from kernel command line\n"
4741 " unlock-kernel-cmdline Remove .pcrlock file for kernel command line\n"
4742 " lock-kernel-initrd FILE Generate a .pcrlock file from an initrd file\n"
4743 " unlock-kernel-initrd Remove .pcrlock file for an initrd file\n"
4744 " lock-raw [FILE] Generate a .pcrlock file from raw data\n"
4745 " unlock-raw Remove .pcrlock file for raw data\n"
4746 "\n%3$sOptions:%4$s\n"
4747 " -h --help Show this help\n"
4748 " --version Print version\n"
4749 " --no-pager Do not pipe output into a pager\n"
4750 " --json=pretty|short|off Generate JSON output\n"
4751 " --raw-description Show raw firmware record data as description in table\n"
4752 " --pcr=NR Generate .pcrlock for specified PCR\n"
4753 " --nv-index=NUMBER Use the specified NV index, instead of a random one\n"
4754 " --components=PATH Directory to read .pcrlock files from\n"
4755 " --location=STRING[:STRING]\n"
4756 " Do not process components beyond this component name\n"
4757 " --recovery-pin=yes Ask for a recovery PIN\n"
4758 " --pcrlock=PATH .pcrlock file to write expected PCR measurement to\n"
4759 " --policy=PATH JSON file to write policy output to\n"
4760 " --force Write policy even if it matches existing policy\n"
4761 "\nSee the %2$s for details.\n",
4762 program_invocation_short_name
,
4772 static int parse_argv(int argc
, char *argv
[]) {
4774 ARG_VERSION
= 0x100,
4777 ARG_RAW_DESCRIPTION
,
4788 static const struct option options
[] = {
4789 { "help", no_argument
, NULL
, 'h' },
4790 { "version", no_argument
, NULL
, ARG_VERSION
},
4791 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
4792 { "json", required_argument
, NULL
, ARG_JSON
},
4793 { "raw-description", no_argument
, NULL
, ARG_RAW_DESCRIPTION
},
4794 { "pcr", required_argument
, NULL
, ARG_PCR
},
4795 { "nv-index", required_argument
, NULL
, ARG_NV_INDEX
},
4796 { "components", required_argument
, NULL
, ARG_COMPONENTS
},
4797 { "location", required_argument
, NULL
, ARG_LOCATION
},
4798 { "recovery-pin", required_argument
, NULL
, ARG_RECOVERY_PIN
},
4799 { "pcrlock", required_argument
, NULL
, ARG_PCRLOCK
},
4800 { "policy", required_argument
, NULL
, ARG_POLICY
},
4801 { "force", no_argument
, NULL
, ARG_FORCE
},
4805 bool auto_location
= true;
4811 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
4815 help(0, NULL
, NULL
);
4822 arg_pager_flags
|= PAGER_DISABLE
;
4826 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4831 case ARG_RAW_DESCRIPTION
:
4832 arg_raw_description
= true;
4836 r
= tpm2_parse_pcr_argument_to_mask(optarg
, &arg_pcr_mask
);
4838 return log_error_errno(r
, "Failed to parse PCR specification: %s", optarg
);
4844 if (isempty(optarg
))
4849 r
= safe_atou32_full(optarg
, 16, &u
);
4851 return log_error_errno(r
, "Failed to parse --nv-index= argument: %s", optarg
);
4853 if (u
< TPM2_NV_INDEX_FIRST
|| u
> TPM2_NV_INDEX_LAST
)
4854 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument for --nv-index= outside of valid range 0x%" PRIx32
"…0x%" PRIx32
": 0x%" PRIx32
,
4855 TPM2_NV_INDEX_FIRST
, TPM2_NV_INDEX_LAST
, u
);
4861 case ARG_COMPONENTS
: {
4862 _cleanup_free_
char *p
= NULL
;
4864 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &p
);
4868 r
= strv_consume(&arg_components
, TAKE_PTR(p
));
4875 case ARG_LOCATION
: {
4876 _cleanup_free_
char *start
= NULL
, *end
= NULL
;
4879 auto_location
= false;
4881 if (isempty(optarg
)) {
4882 arg_location_start
= mfree(arg_location_start
);
4883 arg_location_end
= mfree(arg_location_end
);
4887 e
= strchr(optarg
, ':');
4889 start
= strndup(optarg
, e
- optarg
);
4893 end
= strdup(e
+ 1);
4897 start
= strdup(optarg
);
4901 end
= strdup(optarg
);
4906 if (!filename_is_valid(start
))
4907 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", start
);
4908 if (!filename_is_valid(end
))
4909 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", end
);
4911 free_and_replace(arg_location_start
, start
);
4912 free_and_replace(arg_location_end
, end
);
4916 case ARG_RECOVERY_PIN
:
4917 r
= parse_boolean_argument("--recovery-pin", optarg
, &arg_recovery_pin
);
4923 if (isempty(optarg
) || streq(optarg
, "-"))
4924 arg_pcrlock_path
= mfree(arg_pcrlock_path
);
4926 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_pcrlock_path
);
4931 arg_pcrlock_auto
= false;
4935 if (isempty(optarg
) || streq(optarg
, "-"))
4936 arg_policy_path
= mfree(arg_policy_path
);
4938 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_policy_path
);
4953 assert_not_reached();
4956 if (auto_location
) {
4957 assert(!arg_location_start
);
4958 assert(!arg_location_end
);
4960 arg_location_start
= strdup("760-");
4961 if (!arg_location_start
)
4964 arg_location_end
= strdup("940-");
4965 if (!arg_location_end
)
4972 static int pcrlock_main(int argc
, char *argv
[]) {
4973 static const Verb verbs
[] = {
4974 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
4975 { "log", VERB_ANY
, 1, VERB_DEFAULT
, verb_show_log
},
4976 { "cel", VERB_ANY
, 1, 0, verb_show_cel
},
4977 { "list-components", VERB_ANY
, 1, 0, verb_list_components
},
4978 { "predict", VERB_ANY
, 1, 0, verb_predict
},
4979 { "lock-firmware-code", VERB_ANY
, 2, 0, verb_lock_firmware
},
4980 { "unlock-firmware-code", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4981 { "lock-firmware-config", VERB_ANY
, 2, 0, verb_lock_firmware
},
4982 { "unlock-firmware-config", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4983 { "lock-secureboot-policy", VERB_ANY
, 1, 0, verb_lock_secureboot_policy
},
4984 { "unlock-secureboot-policy", VERB_ANY
, 1, 0, verb_unlock_secureboot_policy
},
4985 { "lock-secureboot-authority", VERB_ANY
, 1, 0, verb_lock_secureboot_authority
},
4986 { "unlock-secureboot-authority", VERB_ANY
, 1, 0, verb_unlock_secureboot_authority
},
4987 { "lock-gpt", VERB_ANY
, 2, 0, verb_lock_gpt
},
4988 { "unlock-gpt", VERB_ANY
, 1, 0, verb_unlock_gpt
},
4989 { "lock-pe", VERB_ANY
, 2, 0, verb_lock_pe
},
4990 { "unlock-pe", VERB_ANY
, 1, 0, verb_unlock_simple
},
4991 { "lock-uki", VERB_ANY
, 2, 0, verb_lock_uki
},
4992 { "unlock-uki", VERB_ANY
, 1, 0, verb_unlock_simple
},
4993 { "lock-machine-id", VERB_ANY
, 1, 0, verb_lock_machine_id
},
4994 { "unlock-machine-id", VERB_ANY
, 1, 0, verb_unlock_machine_id
},
4995 { "lock-file-system", VERB_ANY
, 2, 0, verb_lock_file_system
},
4996 { "unlock-file-system", VERB_ANY
, 2, 0, verb_unlock_file_system
},
4997 { "lock-kernel-cmdline", VERB_ANY
, 2, 0, verb_lock_kernel_cmdline
},
4998 { "unlock-kernel-cmdline", VERB_ANY
, 1, 0, verb_unlock_kernel_cmdline
},
4999 { "lock-kernel-initrd", VERB_ANY
, 2, 0, verb_lock_kernel_initrd
},
5000 { "unlock-kernel-initrd", VERB_ANY
, 1, 0, verb_unlock_kernel_initrd
},
5001 { "lock-raw", VERB_ANY
, 2, 0, verb_lock_raw
},
5002 { "unlock-raw", VERB_ANY
, 1, 0, verb_unlock_simple
},
5003 { "make-policy", VERB_ANY
, 1, 0, verb_make_policy
},
5004 { "remove-policy", VERB_ANY
, 1, 0, verb_remove_policy
},
5008 return dispatch_verb(argc
, argv
, verbs
, NULL
);
5011 static int run(int argc
, char *argv
[]) {
5014 log_show_color(true);
5015 log_parse_environment();
5018 r
= parse_argv(argc
, argv
);
5022 return pcrlock_main(argc
, argv
);
5025 DEFINE_MAIN_FUNCTION(run
);