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/generated.pcrlock"
77 #define PCRLOCK_KERNEL_INITRD_PATH "/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/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(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 FOREACH_ARRAY(ii
, c
->variants
, c
->n_variants
) {
1895 EventLogComponentVariant
*i
= *ii
;
1897 if (i
->n_records
== 0) {
1898 /* The empty variant always matches */
1903 r
= event_log_match_component_variant(el
, 0, i
, 0, n_matching
+ n_empty
== 0);
1909 if (!strextend_with_separator(&matching_ids
, ", ", i
->id
))
1914 if (n_matching
+ n_empty
== 0) {
1916 if (arg_location_start
&& strcmp(c
->id
, arg_location_start
) >= 0)
1917 log_info("Didn't find component '%s' in event log, assuming system hasn't reached it yet.", c
->id
);
1919 log_notice("Couldn't find component '%s' in event log.", c
->id
);
1920 el
->n_missing_components
++;
1921 el
->missing_component_pcrs
|= event_log_component_pcrs(c
);
1923 } else if (n_matching
> 1)
1924 log_debug("Found %u possible variants of component '%s' in event log (%s). Proceeding.", n_matching
, c
->id
, matching_ids
);
1928 log_notice("Skipped %u components after location '%s' (%s).", n_skipped
, arg_location_end
, skipped_ids
);
1929 if (el
->n_missing_components
> 0)
1930 log_notice("Unable to recognize %zu components in event log.", el
->n_missing_components
);
1932 return event_log_validate_fully_recognized(el
);
1935 static void hsv_to_rgb(
1936 double h
, double s
, double v
,
1937 uint8_t* ret_r
, uint8_t *ret_g
, uint8_t *ret_b
) {
1939 double c
, x
, m
, r
, g
, b
;
1941 assert(s
>= 0 && s
<= 100);
1942 assert(v
>= 0 && v
<= 100);
1947 c
= (s
/ 100.0) * (v
/ 100.0);
1948 x
= c
* (1 - fabs(fmod(h
/ 60.0, 2) - 1));
1951 if (h
>= 0 && h
< 60)
1952 r
= c
, g
= x
, b
= 0.0;
1953 else if (h
>= 60 && h
< 120)
1954 r
= x
, g
= c
, b
= 0.0;
1955 else if (h
>= 120 && h
< 180)
1956 r
= 0.0, g
= c
, b
= x
;
1957 else if (h
>= 180 && h
< 240)
1958 r
= 0.0, g
= x
, b
= c
;
1959 else if (h
>= 240 && h
< 300)
1960 r
= x
, g
= 0.0, b
= c
;
1962 r
= c
, g
= 0.0, b
= x
;
1964 *ret_r
= (uint8_t) ((r
+ m
) * 255);
1965 *ret_g
= (uint8_t) ((g
+ m
) * 255);
1966 *ret_b
= (uint8_t) ((b
+ m
) * 255);
1969 #define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
1971 static const char *ansi_true_color(uint8_t r
, uint8_t g
, uint8_t b
, char ret
[static ANSI_TRUE_COLOR_MAX
]) {
1972 snprintf(ret
, ANSI_TRUE_COLOR_MAX
, "\x1B[38;2;%u;%u;%um", r
, g
, b
);
1976 static char *color_for_pcr(EventLog
*el
, uint32_t pcr
) {
1977 char color
[ANSI_TRUE_COLOR_MAX
];
1981 assert(pcr
< TPM2_PCRS_MAX
);
1983 if (el
->registers
[pcr
].color
)
1984 return el
->registers
[pcr
].color
;
1986 hsv_to_rgb(360.0 / (TPM2_PCRS_MAX
- 1) * pcr
, 100, 90, &r
, &g
, &b
);
1987 ansi_true_color(r
, g
, b
, color
);
1989 el
->registers
[pcr
].color
= strdup(color
);
1990 return el
->registers
[pcr
].color
;
1993 static int add_algorithm_columns(
1997 const char *json_field_prefix
) {
2004 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2005 const char *n
= tpm2_hash_alg_to_string(*alg
);
2006 _cleanup_free_
char *v
= NULL
;
2009 v
= strjoin(prefix
, " ", n
);
2014 size_t c
= table_get_current_column(table
);
2016 r
= table_add_cell(table
, NULL
, TABLE_HEADER
, v
?: n
);
2018 return table_log_add_error(r
);
2020 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
) &&
2021 el
->primary_algorithm
!= UINT16_MAX
&&
2022 *alg
!= el
->primary_algorithm
)
2023 (void) table_hide_column_from_display(table
, c
);
2025 _cleanup_free_
char *j
= NULL
;
2026 if (json_field_prefix
) {
2027 _cleanup_free_
char *m
= strdup(n
);
2031 j
= strjoin(json_field_prefix
, ascii_strupper(m
));
2036 (void) table_set_json_field_name(table
, c
, j
?: n
);
2042 static int show_log_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2043 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2048 table
= table_new_raw(5 + el
->n_algorithms
+ 4);
2052 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2054 r
= table_add_many(table
,
2055 TABLE_HEADER
, "pcr",
2056 TABLE_SET_ALIGN_PERCENT
, 100,
2058 TABLE_HEADER
, "pcrname",
2059 TABLE_HEADER
, "event",
2060 TABLE_HEADER
, "match",
2061 TABLE_SET_ALIGN_PERCENT
, 100);
2063 return table_log_add_error(r
);
2065 r
= add_algorithm_columns(el
, table
, NULL
, NULL
);
2069 size_t phase_column
= table_get_current_column(table
);
2071 r
= table_add_many(table
,
2072 TABLE_HEADER
, "F/U",
2073 TABLE_HEADER
, "source",
2074 TABLE_HEADER
, "component",
2075 TABLE_HEADER
, "description");
2077 return table_log_add_error(r
);
2079 (void) table_hide_column_from_display(table
, table_get_columns(table
) - 3); /* hide source */
2081 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2082 (void) table_hide_column_from_display(table
, (size_t) 1); /* hide color block column */
2084 (void) table_set_json_field_name(table
, phase_column
, "phase");
2086 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2087 EventLogRecord
*record
= *rr
;
2089 r
= table_add_many(table
,
2090 TABLE_UINT32
, record
->pcr
,
2091 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2092 TABLE_SET_COLOR
, color_for_pcr(el
, record
->pcr
),
2093 TABLE_STRING
, tpm2_pcr_index_to_string(record
->pcr
));
2095 return table_log_add_error(r
);
2097 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2100 et
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2102 r
= table_add_cell(table
, NULL
, TABLE_STRING
, et
);
2104 r
= table_add_cell(table
, NULL
, TABLE_UINT32_HEX
, &record
->firmware_event_type
);
2105 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
))
2106 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tpm2_userspace_event_type_to_string(record
->userspace_event_type
));
2108 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2110 return table_log_add_error(r
);
2112 if (record
->event_payload_valid
< 0 || record
->event_payload_valid
== EVENT_PAYLOAD_VALID_DONT_KNOW
)
2113 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2115 r
= table_add_many(table
,
2116 TABLE_BOOLEAN_CHECKMARK
, record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
,
2117 TABLE_SET_COLOR
, ansi_highlight_green_red(record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
));
2119 return table_log_add_error(r
);
2121 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2122 EventLogRecordBank
*bank
;
2124 bank
= event_log_record_find_bank(record
, *alg
);
2126 _cleanup_free_
char *hex
= NULL
;
2128 hex
= hexmem(bank
->hash
.buffer
, bank
->hash
.size
);
2132 r
= table_add_cell(table
, NULL
, TABLE_STRING
, hex
);
2134 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2136 return table_log_add_error(r
);
2139 r
= table_add_many(table
,
2140 TABLE_STRING
, EVENT_LOG_RECORD_IS_FIRMWARE(record
) ? "F" :
2141 EVENT_LOG_RECORD_IS_USERSPACE(record
) ? "U" : NULL
,
2142 TABLE_PATH_BASENAME
, record
->source
,
2143 TABLE_PATH_BASENAME
, record
->n_mapped
> 0 ? record
->mapped
[0]->component
->id
: NULL
,
2144 TABLE_STRING
, record
->description
);
2146 return table_log_add_error(r
);
2150 r
= table_to_json(table
, ret_variant
);
2152 return log_error_errno(r
, "Failed to format table to JSON: %m");
2157 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */true);
2159 return log_error_errno(r
, "Failed to output table: %m");
2164 static bool is_unset_pcr(const void *value
, size_t size
) {
2165 return memeqzero(value
, size
) || memeqbyte(0xffu
, value
, size
);
2168 static bool event_log_pcr_checks_out(const EventLog
*el
, const EventLogRegister
*reg
) {
2172 for (size_t i
= 0; i
< el
->n_algorithms
; i
++)
2173 if (memcmp_nn(reg
->banks
[i
].calculated
.buffer
, reg
->banks
[i
].calculated
.size
,
2174 reg
->banks
[i
].observed
.buffer
, reg
->banks
[i
].observed
.size
) != 0)
2180 static int show_pcr_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2181 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2186 table
= table_new_raw(8 + el
->n_algorithms
*2);
2190 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2192 r
= table_add_many(table
,
2193 TABLE_HEADER
, "pcr",
2194 TABLE_SET_ALIGN_PERCENT
, 100,
2195 TABLE_HEADER
, "", /* color block column */
2196 TABLE_HEADER
, "", /* emoji column */
2197 TABLE_HEADER
, "pcrname",
2198 TABLE_HEADER
, "count",
2199 TABLE_SET_ALIGN_PERCENT
, 100,
2201 TABLE_SET_ALIGN_PERCENT
, 100,
2203 TABLE_SET_ALIGN_PERCENT
, 100,
2205 TABLE_SET_ALIGN_PERCENT
, 100);
2207 return table_log_add_error(r
);
2209 r
= add_algorithm_columns(el
, table
, "Calculated", "calculated");
2213 r
= add_algorithm_columns(el
, table
, "Observed", "observed");
2217 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2218 (void) table_hide_column_from_display(table
, (size_t) 1, (size_t) 2); /* hide color block and emoji column */
2219 else if (!emoji_enabled())
2220 (void) table_hide_column_from_display(table
, (size_t) 2);
2222 (void) table_set_json_field_name(table
, 5, "hashMatchesEventLog");
2223 (void) table_set_json_field_name(table
, 6, "allEventsMatched");
2224 (void) table_set_json_field_name(table
, 7, "noMissingComponents");
2226 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2227 /* Check if the PCR hash value matches the event log data */
2228 bool hash_match
= event_log_pcr_checks_out(el
, el
->registers
+ pcr
);
2230 /* Whether all records in this PCR have a matching component */
2231 bool fully_recognized
= el
->registers
[pcr
].fully_recognized
;
2233 /* Whether any unmatched components touch this PCR */
2234 bool missing_components
= FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
);
2236 const char *emoji
= special_glyph(
2237 !hash_match
? SPECIAL_GLYPH_DEPRESSED_SMILEY
:
2238 !fully_recognized
? SPECIAL_GLYPH_UNHAPPY_SMILEY
:
2239 missing_components
? SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
:
2240 SPECIAL_GLYPH_HAPPY_SMILEY
);
2242 r
= table_add_many(table
,
2244 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2245 TABLE_SET_COLOR
, color_for_pcr(el
, pcr
),
2246 TABLE_STRING
, emoji
,
2247 TABLE_STRING
, tpm2_pcr_index_to_string(pcr
));
2249 return table_log_add_error(r
);
2251 if (el
->registers
[pcr
].n_measurements
> 0)
2252 r
= table_add_cell(table
, NULL
, TABLE_UINT
, &el
->registers
[pcr
].n_measurements
);
2254 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2256 return table_log_add_error(r
);
2258 r
= table_add_many(table
,
2259 TABLE_BOOLEAN_CHECKMARK
, hash_match
,
2260 TABLE_SET_COLOR
, ansi_highlight_green_red(hash_match
),
2261 TABLE_BOOLEAN_CHECKMARK
, fully_recognized
,
2262 TABLE_SET_COLOR
, ansi_highlight_green_red(fully_recognized
),
2263 TABLE_BOOLEAN_CHECKMARK
, !missing_components
,
2264 TABLE_SET_COLOR
, ansi_highlight_green_red(!missing_components
));
2266 return table_log_add_error(r
);
2268 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2271 color
= is_unset_pcr(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
) ? ANSI_GREY
: NULL
;
2273 if (el
->registers
[pcr
].banks
[i
].calculated
.size
> 0) {
2274 _cleanup_free_
char *hex
= NULL
;
2276 hex
= hexmem(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
);
2280 r
= table_add_many(table
,
2282 TABLE_SET_COLOR
, color
);
2284 r
= table_add_many(table
,
2286 TABLE_SET_COLOR
, color
);
2288 return table_log_add_error(r
);
2291 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2292 _cleanup_free_
char *hex
= NULL
;
2295 hex
= hexmem(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
);
2299 color
= !hash_match
? ANSI_HIGHLIGHT_RED
:
2300 is_unset_pcr(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
) ? ANSI_GREY
: NULL
;
2302 r
= table_add_many(table
,
2304 TABLE_SET_COLOR
, color
);
2306 return table_log_add_error(r
);
2311 r
= table_to_json(table
, ret_variant
);
2313 return log_error_errno(r
, "Failed to format table to JSON: %m");
2318 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2320 return log_error_errno(r
, "Failed to output table: %m");
2322 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2324 "%sLegend: H → PCR hash value matches event log%s\n"
2325 "%s R → All event log records for this PCR have a matching component%s\n"
2326 "%s C → No components that couldn't be matched with log records affect this PCR%s\n",
2327 ansi_grey(), ansi_normal(), /* less on small screens automatically resets the color after long lines, hence we set it anew for each line */
2328 ansi_grey(), ansi_normal(),
2329 ansi_grey(), ansi_normal());
2334 static int event_determine_primary_algorithm(EventLog
*el
) {
2337 if (el
->n_algorithms
== 0) {
2338 /* Nothing loaded to make the decision on? Then pick SHA256 */
2339 el
->primary_algorithm
= TPM2_ALG_SHA256
;
2343 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2344 /* If we have SHA256, focus on that that */
2346 if (*alg
== TPM2_ALG_SHA256
) {
2347 el
->primary_algorithm
= *alg
;
2352 /* Otherwise show the "best" (i.e. the one with the highest id value) */
2353 el
->primary_algorithm
= el
->algorithms
[el
->n_algorithms
-1];
2357 static int event_log_load_and_process(EventLog
**ret
) {
2358 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2361 el
= event_log_new();
2365 r
= event_log_add_algorithms_from_environment(el
);
2369 r
= event_log_load(el
);
2373 r
= event_log_read_pcrs(el
);
2377 r
= event_log_calculate_pcrs(el
);
2381 r
= event_log_validate_record_hashes(el
);
2385 r
= event_determine_primary_algorithm(el
);
2389 r
= event_log_load_components(el
);
2393 r
= event_log_map_components(el
);
2397 *ret
= TAKE_PTR(el
);
2401 static int verb_show_log(int argc
, char *argv
[], void *userdata
) {
2402 _cleanup_(json_variant_unrefp
) JsonVariant
*log_table
= NULL
, *pcr_table
= NULL
;
2403 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2404 bool want_json
= !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
);
2407 r
= event_log_load_and_process(&el
);
2414 r
= show_log_table(el
, want_json
? &log_table
: NULL
);
2421 r
= show_pcr_table(el
, want_json
? &pcr_table
: NULL
);
2426 _cleanup_(json_variant_unrefp
) JsonVariant
*object
= NULL
;
2428 r
= json_build(&object
, JSON_BUILD_OBJECT(
2429 JSON_BUILD_PAIR_VARIANT("log", log_table
),
2430 JSON_BUILD_PAIR_VARIANT("pcrs", pcr_table
)));
2432 return log_error_errno(r
, "Failed to generate combined object: %m");
2434 r
= json_variant_dump(object
, arg_json_format_flags
, stdout
, /* prefix= */ NULL
);
2436 return log_error_errno(r
, "Failed to dump JSON object: %m");
2442 static int verb_show_cel(int argc
, char *argv
[], void *userdata
) {
2443 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2444 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2445 uint64_t recnum
= 0;
2448 el
= event_log_new();
2452 r
= event_log_load(el
);
2456 /* Output the event log in TCG CEL-JSON. */
2458 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2459 _cleanup_(json_variant_unrefp
) JsonVariant
*ja
= NULL
, *fj
= NULL
;
2460 EventLogRecord
*record
= *rr
;
2461 JsonVariant
*cd
= NULL
;
2462 const char *ct
= NULL
;
2464 LIST_FOREACH(banks
, bank
, record
->banks
) {
2465 r
= json_variant_append_arrayb(
2466 &ja
, JSON_BUILD_OBJECT(
2467 JSON_BUILD_PAIR_STRING("hashAlg", tpm2_hash_alg_to_string(bank
->algorithm
)),
2468 JSON_BUILD_PAIR_HEX("digest", bank
->hash
.buffer
, bank
->hash
.size
)));
2470 return log_error_errno(r
, "Failed to append CEL digest entry: %m");
2474 r
= json_variant_new_array(&ja
, NULL
, 0);
2476 return log_error_errno(r
, "Failed to allocate JSON array: %m");
2479 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2480 _cleanup_free_
char *et
= NULL
;
2483 z
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2485 _cleanup_free_
char *b
= NULL
;
2487 b
= strreplace(z
, "-", "_");
2491 et
= strjoin("EV_", ascii_strupper(b
));
2494 } else if (asprintf(&et
, "%" PRIu32
, record
->firmware_event_type
) < 0)
2497 r
= json_build(&fj
, JSON_BUILD_OBJECT(
2498 JSON_BUILD_PAIR_STRING("event_type", et
),
2499 JSON_BUILD_PAIR_HEX("event_data", record
->firmware_payload
, record
->firmware_payload_size
)));
2501 return log_error_errno(r
, "Failed to build firmware event data: %m");
2504 ct
= "pcclient_std";
2505 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
)) {
2506 cd
= record
->userspace_content
;
2510 r
= json_variant_append_arrayb(&array
,
2512 JSON_BUILD_PAIR_UNSIGNED("pcr", record
->pcr
),
2513 JSON_BUILD_PAIR_UNSIGNED("recnum", ++recnum
),
2514 JSON_BUILD_PAIR_VARIANT("digests", ja
),
2515 JSON_BUILD_PAIR_CONDITION(ct
, "content_type", JSON_BUILD_STRING(ct
)),
2516 JSON_BUILD_PAIR_CONDITION(cd
, "content", JSON_BUILD_VARIANT(cd
))));
2518 return log_error_errno(r
, "Failed to append CEL record: %m");
2521 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
2522 pager_open(arg_pager_flags
);
2524 json_variant_dump(array
, arg_json_format_flags
|JSON_FORMAT_EMPTY_ARRAY
, stdout
, NULL
);
2528 static int verb_list_components(int argc
, char *argv
[], void *userdata
) {
2529 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2530 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2535 } loc
= BEFORE_LOCATION
;
2538 el
= event_log_new();
2542 r
= event_log_add_algorithms_from_environment(el
);
2546 r
= event_determine_primary_algorithm(el
);
2550 r
= event_log_load_components(el
);
2554 table
= table_new("id", "variants");
2558 FOREACH_ARRAY(c
, el
->components
, el
->n_components
) {
2560 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2561 _cleanup_free_
char *marker
= NULL
;
2565 case BEFORE_LOCATION
:
2566 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2567 loc
= AFTER_LOCATION
;
2568 marker
= strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), " location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_LEFT
));
2569 } else if (arg_location_start
&& strcmp((*c
)->id
, arg_location_start
) >= 0) {
2570 loc
= BETWEEN_LOCATION
;
2571 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_TOP
), " start location '", arg_location_start
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_DOWN
));
2576 case BETWEEN_LOCATION
:
2577 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2578 loc
= AFTER_LOCATION
;
2579 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), " end location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_UP
));
2583 case AFTER_LOCATION
:
2588 r
= table_add_many(table
,
2589 TABLE_STRING
, marker
,
2590 TABLE_SET_COLOR
, ANSI_GREY
,
2593 return table_log_add_error(r
);
2597 FOREACH_ARRAY(variant
, (*c
)->variants
, (*c
)->n_variants
) {
2598 r
= table_add_many(table
,
2599 TABLE_STRING
, (*c
)->id
,
2600 TABLE_PATH
, (*variant
)->path
);
2602 return table_log_add_error(r
);
2606 if (table_get_rows(table
) > 1 || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2607 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2609 return log_error_errno(r
, "Failed to output table: %m");
2612 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2613 if (table_get_rows(table
) > 1)
2614 printf("\n%zu components listed.\n", table_get_rows(table
) - 1);
2616 printf("No components defined.\n");
2622 static int event_log_pcr_mask_checks_out(EventLog
*el
, uint32_t mask
) {
2625 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2627 if (!FLAGS_SET(mask
, UINT32_C(1) << pcr
))
2630 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
))
2631 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Event log for PCR %" PRIu32
" does not match PCR state, refusing.", pcr
);
2637 static int make_pcrlock_record(
2641 JsonVariant
**ret_record
) {
2643 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
2646 assert(data
|| data_size
== 0);
2649 if (data_size
== SIZE_MAX
)
2650 data_size
= strlen(data
);
2652 /* Generates a .pcrlock record for the given PCR and data/data size. This is a subset of TCG CEL. */
2654 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
2655 _cleanup_free_
unsigned char *hash
= NULL
;
2657 unsigned hash_usize
;
2661 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
2662 assert_se(md
= EVP_get_digestbyname(a
));
2663 hash_ssize
= EVP_MD_size(md
);
2664 assert_se(hash_ssize
> 0);
2665 hash_usize
= hash_ssize
;
2667 hash
= malloc(hash_usize
);
2671 if (EVP_Digest(data
, data_size
, hash
, &hash_usize
, md
, NULL
) != 1)
2672 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data with algorithm '%s'.", a
);
2674 r
= json_variant_append_arrayb(
2677 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
2678 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_usize
))));
2680 return log_error_errno(r
, "Failed to build JSON digest object: %m");
2683 r
= json_build(ret_record
,
2685 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(pcr
)),
2686 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
2688 return log_error_errno(r
, "Failed to build record object: %m");
2693 static const char *pcrlock_path(const char *default_pcrlock_path
) {
2694 return arg_pcrlock_path
?: arg_pcrlock_auto
? default_pcrlock_path
: NULL
;
2697 static int write_pcrlock(JsonVariant
*array
, const char *default_pcrlock_path
) {
2698 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *a
= NULL
;
2699 _cleanup_fclose_
FILE *f
= NULL
;
2704 r
= json_variant_new_array(&a
, NULL
, 0);
2706 return log_error_errno(r
, "Failed to allocate empty array: %m");
2711 r
= json_build(&v
, JSON_BUILD_OBJECT(
2712 JSON_BUILD_PAIR("records", JSON_BUILD_VARIANT(array
))));
2714 return log_error_errno(r
, "Failed to build JSON object: %m");
2716 p
= pcrlock_path(default_pcrlock_path
);
2718 (void) mkdir_parents_label(p
, 0755);
2722 return log_error_errno(errno
, "Failed to open %s for writing: %m", p
);
2725 r
= json_variant_dump(v
, arg_json_format_flags
, f
?: stdout
, /* prefix= */ NULL
);
2727 return log_error_errno(r
, "Failed to output JSON object: %m");
2730 log_info("%s written.", p
);
2735 static int unlink_pcrlock(const char *default_pcrlock_path
) {
2738 p
= pcrlock_path(default_pcrlock_path
);
2740 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No .pcrlock path specified, refusing.");
2742 if (unlink(p
) < 0) {
2743 if (errno
!= ENOENT
)
2744 return log_error_errno(errno
, "Failed to delete %s: %m", p
);
2746 log_info("%s already deleted.", p
);
2748 log_info("%s deleted.", p
);
2750 (void) rmdir_parents(p
, "/var/lib");
2755 static int verb_lock_raw(int argc
, char *argv
[], void *userdata
) {
2756 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2757 _cleanup_free_
char *data
= NULL
;
2758 _cleanup_fclose_
FILE *f
= NULL
;
2762 if (arg_pcr_mask
== 0)
2763 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No PCR specified, refusing.");
2766 f
= fopen(argv
[1], "re");
2768 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
2771 r
= read_full_stream(f
?: stdin
, &data
, &size
);
2773 return log_error_errno(r
, "Failed to read data from stdin: %m");
2775 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
2776 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2778 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
2781 r
= make_pcrlock_record(i
, data
, size
, &record
);
2785 r
= json_variant_append_array(&array
, record
);
2787 return log_error_errno(r
, "Failed to append to JSON array: %m");
2790 return write_pcrlock(array
, NULL
);
2793 static int verb_unlock_simple(int argc
, char *argv
[], void *userdata
) {
2794 return unlink_pcrlock(NULL
);
2797 static int verb_lock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2798 static const struct {
2801 int synthesize_empty
; /* 0 → fail, > 0 → synthesize empty db, < 0 → skip */
2803 { EFI_VENDOR_GLOBAL
, "SecureBoot", 0 },
2804 { EFI_VENDOR_GLOBAL
, "PK", 0 },
2805 { EFI_VENDOR_GLOBAL
, "KEK", 0 },
2806 { EFI_VENDOR_DATABASE
, "db", 1 },
2807 { EFI_VENDOR_DATABASE
, "dbx", 1 },
2808 { EFI_VENDOR_DATABASE
, "dbt", -1 },
2809 { EFI_VENDOR_DATABASE
, "dbr", -1 },
2812 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2815 /* Generates expected records from the current SecureBoot state, as readable in the EFI variables
2818 FOREACH_ARRAY(vv
, variables
, ELEMENTSOF(variables
)) {
2819 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2821 _cleanup_free_
char *name
= NULL
;
2822 if (asprintf(&name
, "%s-" SD_ID128_UUID_FORMAT_STR
, vv
->name
, SD_ID128_FORMAT_VAL(vv
->id
)) < 0)
2825 _cleanup_free_
void *data
= NULL
;
2827 r
= efi_get_variable(name
, NULL
, &data
, &data_size
);
2829 if (r
!= -ENOENT
|| vv
->synthesize_empty
== 0)
2830 return log_error_errno(r
, "Failed to read EFI variable '%s': %m", name
);
2831 if (vv
->synthesize_empty
< 0)
2834 /* If the main database variables are not set we don't consider this an error, but
2835 * measure an empty database instead. */
2836 log_debug("EFI variable %s is not set, synthesizing empty variable for measurement.", name
);
2840 _cleanup_free_ char16_t
* name16
= utf8_to_utf16(vv
->name
, SIZE_MAX
);
2843 size_t name16_bytes
= char16_strlen(name16
) * 2;
2845 size_t vdata_size
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + name16_bytes
+ data_size
;
2846 _cleanup_free_ UEFI_VARIABLE_DATA
*vdata
= malloc(vdata_size
);
2850 *vdata
= (UEFI_VARIABLE_DATA
) {
2851 .unicodeNameLength
= name16_bytes
/ 2,
2852 .variableDataLength
= data_size
,
2855 efi_id128_to_guid(vv
->id
, vdata
->variableName
);
2856 memcpy(mempcpy(vdata
->unicodeName
, name16
, name16_bytes
), data
, data_size
);
2858 r
= make_pcrlock_record(TPM2_PCR_SECURE_BOOT_POLICY
/* =7 */, vdata
, vdata_size
, &record
);
2862 r
= json_variant_append_array(&array
, record
);
2864 return log_error_errno(r
, "Failed to append to JSON array: %m");
2867 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_POLICY_PATH
);
2870 static int verb_unlock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2871 return unlink_pcrlock(PCRLOCK_SECUREBOOT_POLICY_PATH
);
2874 static int event_log_record_is_secureboot_variable(EventLogRecord
*rec
, sd_id128_t uuid
, const char *name
) {
2875 _cleanup_free_
char *found_name
= NULL
;
2876 sd_id128_t found_uuid
;
2882 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2885 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2888 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2891 if (rec
->firmware_event_type
!= EV_EFI_VARIABLE_DRIVER_CONFIG
)
2894 r
= event_log_record_parse_variable_data(rec
, &found_uuid
, &found_name
);
2900 if (!sd_id128_equal(found_uuid
, uuid
))
2903 return streq(found_name
, name
);
2906 static bool event_log_record_is_secureboot_authority(EventLogRecord
*rec
) {
2909 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2912 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2915 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2918 return rec
->firmware_event_type
== EV_EFI_VARIABLE_AUTHORITY
;
2921 static int event_log_ensure_secureboot_consistency(EventLog
*el
) {
2922 static const struct {
2927 { EFI_VENDOR_GLOBAL
, "SecureBoot", true },
2928 { EFI_VENDOR_GLOBAL
, "PK", true },
2929 { EFI_VENDOR_GLOBAL
, "KEK", true },
2930 { EFI_VENDOR_DATABASE
, "db", true },
2931 { EFI_VENDOR_DATABASE
, "dbx", true },
2932 { EFI_VENDOR_DATABASE
, "dbt", false },
2933 { EFI_VENDOR_DATABASE
, "dbr", false },
2934 // FIXME: ensure we also find the separator here
2937 EventLogRecord
*records
[ELEMENTSOF(table
)] = {};
2938 EventLogRecord
*first_authority
= NULL
;
2942 /* Ensures that the PCR 7 records are complete and in order. Before we lock down PCR 7 we want to
2943 * ensure its state is actually consistent. */
2945 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2946 EventLogRecord
*rec
= *rr
;
2947 size_t found
= SIZE_MAX
;
2949 if (event_log_record_is_secureboot_authority(rec
)) {
2950 if (first_authority
)
2953 first_authority
= rec
;
2954 // FIXME: also check that each authority record's data is also listed in 'db'
2958 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2959 if (event_log_record_is_secureboot_variable(rec
, table
[i
].id
, table
[i
].name
)) {
2963 if (found
== SIZE_MAX
)
2966 /* Require the authority records always come *after* database measurements */
2967 if (first_authority
)
2968 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "SecureBoot authority before variable, refusing.");
2970 /* Check for duplicates */
2972 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Duplicate '%s' record, refusing.", rec
->description
);
2974 /* Check for order */
2975 for (size_t j
= found
+ 1; j
< ELEMENTSOF(table
); j
++)
2977 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'%s' record before '%s' record, refusing.", records
[j
]->description
, rec
->description
);
2979 records
[found
] = rec
;
2982 /* Check for existence */
2983 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2984 if (table
[i
].required
&& !records
[i
])
2985 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Required record '%s' not found, refusing.", table
[i
].name
);
2987 /* At this point we know that all required variables have been measured, in the right order. */
2991 static int verb_lock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
2992 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2993 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2996 /* Lock down the EV_EFI_VARIABLE_AUTHORITY records from the existing log. Note that there's not too
2997 * much value in locking this down too much, since it stores only the result of the primary database
2998 * checks, and that's what we should bind policy to. Moreover it's hard to predict, since extension
2999 * card firmware validation will result in additional records here. */
3001 if (!is_efi_secure_boot()) {
3002 log_info("SecureBoot disabled, not generating authority .pcrlock file.");
3003 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3006 el
= event_log_new();
3010 r
= event_log_add_algorithms_from_environment(el
);
3014 r
= event_log_load(el
);
3018 r
= event_log_read_pcrs(el
);
3022 r
= event_log_calculate_pcrs(el
);
3026 /* Before we base anything on the event log records, let's check that the event log state checks
3029 r
= event_log_pcr_mask_checks_out(el
, UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY
);
3033 r
= event_log_validate_record_hashes(el
);
3037 r
= event_log_ensure_secureboot_consistency(el
);
3041 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3042 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3043 EventLogRecord
*rec
= *rr
;
3045 if (!event_log_record_is_secureboot_authority(rec
))
3048 log_debug("Locking down authority '%s'.", strna(rec
->description
));
3050 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3051 r
= json_variant_append_arrayb(
3054 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3055 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3057 return log_error_errno(r
, "Failed to build digests array: %m");
3060 r
= json_variant_append_arrayb(
3063 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3064 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3066 return log_error_errno(r
, "Failed to build record array: %m");
3069 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3072 static int verb_unlock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
3073 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3076 static int verb_lock_gpt(int argc
, char *argv
[], void *userdata
) {
3077 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *record
= NULL
;
3078 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
3079 uint8_t h
[2 * 4096]; /* space for at least two 4K sectors. GPT header should definitely be in here */
3080 uint64_t start
, n_members
, member_size
;
3081 _cleanup_close_
int fd
= -EBADF
;
3087 r
= block_device_new_from_path(
3088 argc
>= 2 ? argv
[1] : "/",
3089 BLOCK_DEVICE_LOOKUP_WHOLE_DISK
|BLOCK_DEVICE_LOOKUP_BACKING
|BLOCK_DEVICE_LOOKUP_ORIGINATING
,
3092 return log_error_errno(r
, "Failed to determine root block device: %m");
3094 fd
= sd_device_open(d
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
);
3096 return log_error_errno(fd
, "Failed to open root block device: %m");
3098 n
= pread(fd
, &h
, sizeof(h
), 0);
3100 return log_error_errno(errno
, "Failed to read GPT header of block device: %m");
3101 if ((size_t) n
!= sizeof(h
))
3102 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read trying to read GPT header: %m");
3104 /* Try a couple of sector sizes */
3105 for (size_t sz
= 512; sz
<= 4096; sz
<<= 1) {
3106 assert(sizeof(h
) >= sz
* 2);
3107 p
= (const GptHeader
*) (h
+ sz
); /* 2nd sector */
3109 if (!gpt_header_has_signature(p
))
3113 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
),
3114 "Disk has partition table for multiple sector sizes, refusing.");
3120 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3121 "Disk does not have GPT partition table, refusing.");
3123 p
= (const GptHeader
*) (h
+ found
);
3125 if (le32toh(p
->header_size
) > found
)
3126 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3127 "GPT header size over long (%" PRIu32
"), refusing.", le32toh(p
->header_size
));
3129 start
= le64toh(p
->partition_entry_lba
);
3130 if (start
> UINT64_MAX
/ found
)
3131 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3132 "Partition table start offset overflow, refusing.");
3134 member_size
= le32toh(p
->size_of_partition_entry
);
3135 if (member_size
< sizeof(GptPartitionEntry
))
3136 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3137 "Partition entry size too short, refusing.");
3139 n_members
= le32toh(p
->number_of_partition_entries
);
3140 uint64_t member_bufsz
= n_members
* member_size
;
3141 if (member_bufsz
> 1U*1024U*1024U)
3142 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3143 "Partition table size too large, refusing.");
3145 member_bufsz
= ROUND_UP(member_bufsz
, found
);
3147 _cleanup_free_
void *members
= malloc(member_bufsz
);
3151 n
= pread(fd
, members
, member_bufsz
, start
* found
);
3153 return log_error_errno(errno
, "Failed to read GPT partition table entries: %m");
3154 if ((size_t) n
!= member_bufsz
)
3155 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read while reading GPT partition table entries: %m");
3157 size_t vdata_size
= le32toh(p
->header_size
) + sizeof(le64_t
) + member_size
* n_members
;
3158 _cleanup_free_
void *vdata
= malloc0(vdata_size
);
3162 void *n_measured_entries
= mempcpy(vdata
, p
, sizeof(GptHeader
)); /* n_measured_entries is a 64bit value */
3164 void *qq
= (uint8_t*) n_measured_entries
+ sizeof(le64_t
);
3166 for (uint64_t i
= 0; i
< n_members
; i
++) {
3167 const GptPartitionEntry
*entry
= (const GptPartitionEntry
*) ((const uint8_t*) members
+ (member_size
* i
));
3169 if (memeqzero(entry
->partition_type_guid
, sizeof(entry
->partition_type_guid
)))
3172 qq
= mempcpy(qq
, entry
, member_size
);
3173 unaligned_write_le64(n_measured_entries
, unaligned_read_le64(n_measured_entries
) + 1);
3176 vdata_size
= (uint8_t*) qq
- (uint8_t*) vdata
;
3178 r
= make_pcrlock_record(TPM2_PCR_BOOT_LOADER_CONFIG
/* =5 */, vdata
, vdata_size
, &record
);
3182 r
= json_variant_new_array(&array
, &record
, 1);
3184 return log_error_errno(r
, "Failed to append to JSON array: %m");
3186 return write_pcrlock(array
, PCRLOCK_GPT_PATH
);
3189 static int verb_unlock_gpt(int argc
, char *argv
[], void *userdata
) {
3190 return unlink_pcrlock(PCRLOCK_GPT_PATH
);
3193 static bool event_log_record_is_separator(const EventLogRecord
*rec
) {
3196 /* Recognizes EV_SEPARATOR events */
3198 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3201 if (rec
->firmware_event_type
!= EV_SEPARATOR
)
3204 return rec
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
; /* Insist the record is consistent */
3207 static int event_log_record_is_action_calling_efi_app(const EventLogRecord
*rec
) {
3208 _cleanup_free_
char *d
= NULL
;
3213 /* Recognizes the special EV_EFI_ACTION that is issues when the firmware passes control to the boot loader. */
3215 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3218 if (rec
->pcr
!= TPM2_PCR_BOOT_LOADER_CODE
)
3221 if (rec
->firmware_event_type
!= EV_EFI_ACTION
)
3224 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
) /* Insist the record is consistent */
3227 r
= make_cstring(rec
->firmware_payload
, rec
->firmware_payload_size
, MAKE_CSTRING_ALLOW_TRAILING_NUL
, &d
);
3231 return streq(d
, "Calling EFI Application from Boot Option");
3234 static void enable_json_sse(void) {
3235 /* We shall write this to a single output stream? We have to output two files, hence try to be smart
3236 * and enable JSON SSE */
3238 if (!arg_pcrlock_path
&& arg_pcrlock_auto
)
3241 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_SSE
))
3244 log_notice("Enabling JSON_SEQ mode, since writing two .pcrlock files to single output.");
3245 arg_json_format_flags
|= JSON_FORMAT_SSE
;
3248 static int verb_lock_firmware(int argc
, char *argv
[], void *userdata
) {
3249 _cleanup_(json_variant_unrefp
) JsonVariant
*array_early
= NULL
, *array_late
= NULL
;
3250 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
3251 uint32_t always_mask
, separator_mask
, separator_seen_mask
= 0, action_seen_mask
= 0;
3252 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3257 /* The PCRs we intend to cover. Note that we measure firmware, external *and* boot loader code/config
3258 * here – but the latter only until the "separator" events are seen, which tell us where transition
3259 * into OS boot loader happens. This reflects the fact that on some systems the firmware already
3260 * measures some firmware-supplied apps into PCR 4. (e.g. Thinkpad X1 Gen9) */
3261 if (endswith(argv
[0], "firmware-code")) {
3262 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CODE
) | /* → 0 */
3263 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE
); /* → 2 */
3265 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
; /* → 4 */
3267 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3268 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3270 assert(endswith(argv
[0], "firmware-config"));
3271 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG
) | /* → 1 */
3272 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG
); /* → 3 */
3274 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG
; /* → 5 */
3276 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3277 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3280 el
= event_log_new();
3284 r
= event_log_add_algorithms_from_environment(el
);
3288 r
= event_log_load(el
);
3292 r
= event_log_read_pcrs(el
);
3296 r
= event_log_calculate_pcrs(el
);
3300 r
= event_log_validate_record_hashes(el
);
3304 /* Before we base anything on the event log records for any of the selected PCRs, let's check that
3305 * the event log state checks out for them. */
3307 r
= event_log_pcr_mask_checks_out(el
, always_mask
|separator_mask
);
3311 // FIXME: before doing this, validate ahead-of-time that EV_SEPARATOR records exist for all entries,
3314 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3315 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3316 EventLogRecord
*rec
= *rr
;
3317 uint32_t bit
= UINT32_C(1) << rec
->pcr
;
3319 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3322 if (!FLAGS_SET(always_mask
, bit
) &&
3323 !(FLAGS_SET(separator_mask
, bit
) && !FLAGS_SET(separator_seen_mask
|action_seen_mask
, bit
)))
3326 /* If we hit the separator record, we stop processing the PCRs listed in `separator_mask` */
3327 if (event_log_record_is_separator(rec
)) {
3328 separator_seen_mask
|= bit
;
3332 /* If we hit the special "Calling EFI Application from Boot Option" action we treat this the
3333 * same as a separator here, as that's where firmware passes control to boot loader. Note
3334 * that some EFI implementations forget to generate one of them. */
3335 r
= event_log_record_is_action_calling_efi_app(rec
);
3337 return log_error_errno(r
, "Failed to check if event is 'Calling EFI Application from Boot Option' action: %m");
3339 action_seen_mask
|= bit
;
3343 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3344 r
= json_variant_append_arrayb(
3347 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3348 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3350 return log_error_errno(r
, "Failed to build digests array: %m");
3353 r
= json_variant_append_arrayb(
3354 FLAGS_SET(separator_seen_mask
, bit
) ? &array_late
: &array_early
,
3356 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3357 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3359 return log_error_errno(r
, "Failed to build record array: %m");
3362 r
= write_pcrlock(array_early
, default_pcrlock_early_path
);
3366 return write_pcrlock(array_late
, default_pcrlock_late_path
);
3369 static int verb_unlock_firmware(int argc
, char *argv
[], void *userdata
) {
3370 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3373 if (endswith(argv
[0], "firmware-code")) {
3374 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3375 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3377 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3378 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3381 r
= unlink_pcrlock(default_pcrlock_early_path
);
3385 if (arg_pcrlock_path
) /* if the path is specified don't delete the same thing twice */
3388 r
= unlink_pcrlock(default_pcrlock_late_path
);
3395 static int verb_lock_machine_id(int argc
, char *argv
[], void *userdata
) {
3396 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3397 _cleanup_free_
char *word
= NULL
;
3400 r
= pcrextend_machine_id_word(&word
);
3404 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3408 r
= json_variant_new_array(&array
, &record
, 1);
3410 return log_error_errno(r
, "Failed to create record array: %m");
3412 return write_pcrlock(array
, PCRLOCK_MACHINE_ID_PATH
);
3415 static int verb_unlock_machine_id(int argc
, char *argv
[], void *userdata
) {
3416 return unlink_pcrlock(PCRLOCK_MACHINE_ID_PATH
);
3419 static int pcrlock_file_system_path(const char *normalized_path
, char **ret
) {
3420 _cleanup_free_
char *s
= NULL
;
3422 assert(normalized_path
);
3424 if (path_equal(normalized_path
, "/"))
3425 s
= strdup(PCRLOCK_ROOT_FILE_SYSTEM_PATH
);
3427 /* We reuse the escaping we use for turning paths into unit names */
3428 _cleanup_free_
char *escaped
= NULL
;
3430 assert(normalized_path
[0] == '/');
3431 assert(normalized_path
[1] != '/');
3433 escaped
= unit_name_escape(normalized_path
+ 1);
3437 s
= strjoin(PCRLOCK_FILE_SYSTEM_PATH_PREFIX
, escaped
, ".pcrlock");
3446 static int verb_lock_file_system(int argc
, char *argv
[], void *userdata
) {
3447 const char* paths
[3] = {};
3456 r
= get_block_device("/", &a
);
3458 return log_error_errno(r
, "Failed to get device of root file system: %m");
3460 r
= get_block_device("/var", &b
);
3462 return log_error_errno(r
, "Failed to get device of /var/ file system: %m");
3464 /* if backing device is distinct, then measure /var/ too */
3471 STRV_FOREACH(p
, paths
) {
3472 _cleanup_free_
char *word
= NULL
, *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3473 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3475 r
= pcrextend_file_system_word(*p
, &word
, &normalized_path
);
3479 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3483 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3487 r
= json_variant_new_array(&array
, &record
, 1);
3489 return log_error_errno(r
, "Failed to create record array: %m");
3491 r
= write_pcrlock(array
, pcrlock_file
);
3499 static int verb_unlock_file_system(int argc
, char *argv
[], void *userdata
) {
3500 const char* paths
[3] = {};
3510 STRV_FOREACH(p
, paths
) {
3511 _cleanup_free_
char *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3513 r
= chase(*p
, NULL
, 0, &normalized_path
, NULL
);
3515 return log_error_errno(r
, "Failed to normal path '%s': %m", argv
[1]);
3517 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3521 r
= unlink_pcrlock(pcrlock_file
);
3529 static int verb_lock_pe(int argc
, char *argv
[], void *userdata
) {
3530 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
3531 _cleanup_close_
int fd
= -EBADF
;
3534 // FIXME: Maybe also generate a matching EV_EFI_VARIABLE_AUTHORITY records here for each signature that
3535 // covers this PE plus its hash, as alternatives under the same component name
3538 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3540 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3543 if (arg_pcr_mask
== 0)
3544 arg_pcr_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
;
3546 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
3547 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3549 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
3552 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
3553 _cleanup_free_
void *hash
= NULL
;
3558 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
3559 assert_se(md
= EVP_get_digestbyname(a
));
3561 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &hash
, &hash_size
);
3563 return log_error_errno(r
, "Failed to hash PE binary: %m");
3565 r
= json_variant_append_arrayb(&digests
,
3567 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3568 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_size
))));
3570 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3573 r
= json_variant_append_arrayb(
3576 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i
)),
3577 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3579 return log_error_errno(r
, "Failed to append record object: %m");
3582 return write_pcrlock(array
, NULL
);
3585 typedef void* SectionHashArray
[_UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
];
3587 static void section_hashes_array_done(SectionHashArray
*array
) {
3590 for (size_t i
= 0; i
< _UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
; i
++)
3594 static int verb_lock_uki(int argc
, char *argv
[], void *userdata
) {
3595 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *pe_digests
= NULL
;
3596 _cleanup_(section_hashes_array_done
) SectionHashArray section_hashes
= {};
3597 size_t hash_sizes
[TPM2_N_HASH_ALGORITHMS
];
3598 _cleanup_close_
int fd
= -EBADF
;
3601 if (arg_pcr_mask
!= 0)
3602 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "PCR not configurable for UKI lock down.");
3605 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3607 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3610 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3611 _cleanup_free_
void *peh
= NULL
;
3615 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3616 assert_se(md
= EVP_get_digestbyname(a
));
3618 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &peh
, hash_sizes
+ i
);
3620 return log_error_errno(r
, "Failed to hash PE binary: %m");
3622 r
= json_variant_append_arrayb(
3625 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3626 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(peh
, hash_sizes
[i
]))));
3628 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3630 r
= uki_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, section_hashes
+ (i
* _UNIFIED_SECTION_MAX
), hash_sizes
+ i
);
3632 return log_error_errno(r
, "Failed to UKI hash PE binary: %m");
3635 r
= json_variant_append_arrayb(
3638 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_BOOT_LOADER_CODE
)),
3639 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(pe_digests
))));
3641 return log_error_errno(r
, "Failed to append record object: %m");
3643 for (UnifiedSection section
= 0; section
< _UNIFIED_SECTION_MAX
; section
++) {
3644 _cleanup_(json_variant_unrefp
) JsonVariant
*section_digests
= NULL
, *record
= NULL
;
3646 if (!unified_section_measure(section
))
3649 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3653 hash
= section_hashes
[i
* _UNIFIED_SECTION_MAX
+ section
];
3657 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3659 r
= json_variant_append_arrayb(
3662 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3663 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_sizes
[i
]))));
3665 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3668 if (!section_digests
)
3671 /* So we have digests for this section, hence generate a record for the section name first. */
3672 r
= make_pcrlock_record(TPM2_PCR_KERNEL_BOOT
/* =11 */, unified_sections
[section
], strlen(unified_sections
[section
]) + 1, &record
);
3676 r
= json_variant_append_array(&array
, record
);
3678 return log_error_errno(r
, "Failed to append JSON record array: %m");
3680 /* And then append a record for the section contents digests as well */
3681 r
= json_variant_append_arrayb(
3684 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_KERNEL_BOOT
/* =11 */)),
3685 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(section_digests
))));
3687 return log_error_errno(r
, "Failed to append record object: %m");
3690 return write_pcrlock(array
, NULL
);
3693 static int event_log_reduce_to_safe_pcrs(EventLog
*el
, uint32_t *pcrs
) {
3694 _cleanup_free_
char *dropped
= NULL
, *kept
= NULL
;
3699 /* When we compile a new PCR policy we don't want to bind to PCRs which are fishy for one of three
3702 * 1. The PCR value doesn't match the event log
3703 * 2. The event log for the PCR contains measurements we don't know responsible components for
3704 * 3. The event log for the PCR does not contain measurements for components we know
3706 * This function checks for the three conditions and drops the PCR from the mask.
3709 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
3711 if (!FLAGS_SET(*pcrs
, UINT32_C(1) << pcr
))
3714 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
)) {
3715 log_notice("PCR %" PRIu32
" (%s) value does not match event log. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3719 if (!el
->registers
[pcr
].fully_recognized
) {
3720 log_notice("PCR %" PRIu32
" (%s) event log contains unrecognized measurements. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3724 if (FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
)) {
3725 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
)));
3729 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
)));
3731 if (strextendf_with_separator(&kept
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3737 *pcrs
&= ~(UINT32_C(1) << pcr
);
3739 if (strextendf_with_separator(&dropped
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3744 log_notice("PCRs dropped from protection mask: %s", dropped
);
3746 log_debug("No PCRs dropped from protection mask.");
3749 log_notice("PCRs in protection mask: %s", kept
);
3751 log_notice("No PCRs kept in protection mask.");
3756 static int verb_lock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3757 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3758 _cleanup_free_
char *cmdline
= NULL
;
3762 if (empty_or_dash(argv
[1]))
3763 r
= read_full_stream(stdin
, &cmdline
, NULL
);
3765 r
= read_full_file(argv
[1], &cmdline
, NULL
);
3767 r
= proc_cmdline(&cmdline
);
3769 return log_error_errno(r
, "Failed to read cmdline: %m");
3771 delete_trailing_chars(cmdline
, "\n");
3773 _cleanup_free_ char16_t
*u
= NULL
;
3774 u
= utf8_to_utf16(cmdline
, SIZE_MAX
);
3778 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, u
, char16_strlen(u
)*2+2, &record
);
3782 r
= json_variant_new_array(&array
, &record
, 1);
3784 return log_error_errno(r
, "Failed to create record array: %m");
3786 r
= write_pcrlock(array
, PCRLOCK_KERNEL_CMDLINE_PATH
);
3793 static int verb_unlock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3794 return unlink_pcrlock(PCRLOCK_KERNEL_CMDLINE_PATH
);
3797 static int verb_lock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3798 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3799 _cleanup_free_
void *data
= NULL
;
3800 _cleanup_fclose_
FILE *f
= NULL
;
3805 f
= fopen(argv
[1], "re");
3807 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3810 r
= read_full_stream(f
?: stdin
, (char**) &data
, &size
);
3812 return log_error_errno(r
, "Failed to read data from stdin: %m");
3814 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, data
, size
, &record
);
3818 r
= json_variant_new_array(&array
, &record
, 1);
3820 return log_error_errno(r
, "Failed to create record array: %m");
3822 r
= write_pcrlock(array
, PCRLOCK_KERNEL_INITRD_PATH
);
3829 static int verb_unlock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3830 return unlink_pcrlock(PCRLOCK_KERNEL_INITRD_PATH
);
3833 static int pcr_prediction_add_result(
3834 Tpm2PCRPrediction
*context
,
3835 Tpm2PCRPredictionResult
*result
,
3840 _cleanup_free_ Tpm2PCRPredictionResult
*copy
= NULL
;
3846 copy
= newdup(Tpm2PCRPredictionResult
, result
, 1);
3850 r
= ordered_set_ensure_put(context
->results
+ pcr
, &tpm2_pcr_prediction_result_hash_ops
, copy
);
3851 if (r
== -EEXIST
) /* Multiple identical results for the same PCR are totally expected */
3854 return log_error_errno(r
, "Failed to insert result into set: %m");
3856 log_debug("Added prediction result %u for PCR %" PRIu32
" (path: %s)", ordered_set_size(context
->results
[pcr
]), pcr
, strempty(path
));
3862 static const EVP_MD
* evp_from_tpm2_alg(uint16_t alg
) {
3865 name
= tpm2_hash_alg_to_string(alg
);
3869 return EVP_get_digestbyname(name
);
3872 static int event_log_component_variant_calculate(
3873 Tpm2PCRPrediction
*context
,
3874 Tpm2PCRPredictionResult
*result
,
3875 EventLogComponent
*component
,
3876 EventLogComponentVariant
*variant
,
3887 FOREACH_ARRAY(rr
, variant
->records
, variant
->n_records
) {
3888 EventLogRecord
*rec
= *rr
;
3890 if (rec
->pcr
!= pcr
)
3893 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3894 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*md_ctx
= NULL
;
3895 EventLogRecordBank
*b
;
3897 if (result
->hash
[i
].size
<= 0) /* already invalidated */
3900 b
= event_log_record_find_bank(rec
, tpm2_hash_algorithms
[i
]);
3902 /* Can't calculate, hence invalidate */
3903 result
->hash
[i
] = (TPM2B_DIGEST
) {};
3907 md_ctx
= EVP_MD_CTX_new();
3911 const EVP_MD
*md
= ASSERT_PTR(evp_from_tpm2_alg(tpm2_hash_algorithms
[i
]));
3913 int sz
= EVP_MD_size(md
);
3915 assert((size_t) sz
<= sizeof_field(TPM2B_DIGEST
, buffer
));
3917 assert(sz
== tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]));
3919 assert(result
->hash
[i
].size
== (size_t) sz
);
3920 assert(b
->hash
.size
== (size_t) sz
);
3922 if (EVP_DigestInit_ex(md_ctx
, md
, NULL
) != 1)
3923 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to initialize message digest.");
3925 if (EVP_DigestUpdate(md_ctx
, result
->hash
[i
].buffer
, sz
) != 1)
3926 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash bank value.");
3928 if (EVP_DigestUpdate(md_ctx
, b
->hash
.buffer
, sz
) != 1)
3929 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data value.");
3931 unsigned l
= (unsigned) sz
;
3932 if (EVP_DigestFinal_ex(md_ctx
, result
->hash
[i
].buffer
, &l
) != 1)
3933 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to finalize message digest.");
3935 assert(l
== (unsigned) sz
);
3938 /* This is a valid result once we hit the start location */
3939 if (arg_location_start
&& strcmp(component
->id
, arg_location_start
) >= 0) {
3940 r
= pcr_prediction_add_result(context
, result
, pcr
, path
, rr
- variant
->records
);
3949 static int event_log_predict_pcrs(
3951 Tpm2PCRPrediction
*context
,
3952 Tpm2PCRPredictionResult
*parent_result
,
3953 size_t component_index
,
3957 EventLogComponent
*component
;
3962 assert(parent_result
);
3964 /* Check if we reached the end of the components, generate a result, and backtrack */
3965 if (component_index
>= el
->n_components
||
3966 (arg_location_end
&& strcmp(el
->components
[component_index
]->id
, arg_location_end
) > 0)) {
3967 r
= pcr_prediction_add_result(context
, parent_result
, pcr
, path
, /* offset= */ 0);
3974 component
= ASSERT_PTR(el
->components
[component_index
]);
3976 FOREACH_ARRAY(ii
, component
->variants
, component
->n_variants
) {
3977 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
3978 EventLogComponentVariant
*variant
= *ii
;
3979 _cleanup_free_
char *subpath
= NULL
;
3981 /* Operate on a copy of the result */
3984 subpath
= strjoin(path
, ":", component
->id
);
3986 subpath
= strdup(component
->id
);
3990 if (!streq(component
->id
, variant
->id
))
3991 if (!strextend(&subpath
, "@", variant
->id
))
3994 result
= newdup(Tpm2PCRPredictionResult
, parent_result
, 1);
3998 r
= event_log_component_variant_calculate(
4008 r
= event_log_predict_pcrs(
4012 component_index
+ 1, /* Next component */
4024 static ssize_t
event_log_calculate_component_combinations(EventLog
*el
) {
4028 FOREACH_ARRAY(cc
, el
->components
, el
->n_components
) {
4029 EventLogComponent
*c
= *cc
;
4031 /* Overflow check */
4032 if (c
->n_variants
> (size_t) (SSIZE_MAX
/count
))
4033 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Too many component combinations.");
4035 count
*= c
->n_variants
;
4041 static int event_log_show_predictions(Tpm2PCRPrediction
*context
, uint16_t alg
) {
4046 pager_open(arg_pager_flags
);
4048 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
4049 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
4051 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
4052 _cleanup_(json_variant_unrefp
) JsonVariant
*aj
= NULL
;
4054 r
= tpm2_pcr_prediction_to_json(
4056 tpm2_hash_algorithms
[i
],
4061 if (json_variant_elements(aj
) == 0)
4064 r
= json_variant_set_field(
4066 tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]),
4069 return log_error_errno(r
, "Failed to add prediction bank to object: %m");
4073 r
= json_variant_new_object(&j
, NULL
, 0);
4075 return log_error_errno(r
, "Failed to allocated empty object: %m");
4078 json_variant_dump(j
, arg_json_format_flags
, /* f= */ NULL
, /* prefix= */ NULL
);
4082 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4083 Tpm2PCRPredictionResult
*result
;
4084 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4087 if (ordered_set_isempty(context
->results
[pcr
])) {
4088 printf("No results for PCR %u (%s).\n", pcr
, tpm2_pcr_index_to_string(pcr
));
4092 printf("%sResults for PCR %u (%s):%s\n", ansi_underline(), pcr
, tpm2_pcr_index_to_string(pcr
), ansi_normal());
4094 ORDERED_SET_FOREACH(result
, context
->results
[pcr
]) {
4096 _cleanup_free_
char *aa
= NULL
, *h
= NULL
;
4099 TPM2B_DIGEST
*hash
= tpm2_pcr_prediction_result_get_hash(result
, alg
);
4103 a
= ASSERT_PTR(tpm2_hash_alg_to_string(alg
));
4110 h
= hexmem(hash
->buffer
, hash
->size
);
4114 printf(" %s%-6s:%s %s\n", ansi_grey(), aa
, ansi_normal(), h
);
4121 static int tpm2_pcr_prediction_run(
4123 Tpm2PCRPrediction
*context
) {
4130 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4131 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
4133 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4136 result
= new0(Tpm2PCRPredictionResult
, 1);
4140 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++)
4141 event_log_initial_pcr_state(el
, pcr
, tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]), result
->hash
+ i
);
4143 r
= event_log_predict_pcrs(
4147 /* component_index= */ 0,
4157 static int verb_predict(int argc
, char *argv
[], void *userdata
) {
4158 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction context
= {
4159 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4161 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4165 r
= event_log_load_and_process(&el
);
4169 count
= event_log_calculate_component_combinations(el
);
4173 log_info("%zi combinations of components.", count
);
4175 r
= event_log_reduce_to_safe_pcrs(el
, &context
.pcrs
);
4179 r
= tpm2_pcr_prediction_run(el
, &context
);
4183 return event_log_show_predictions(&context
, el
->primary_algorithm
);
4186 static int remove_policy_file(const char *path
) {
4189 if (unlink(path
) < 0) {
4190 if (errno
== ENOENT
)
4193 return log_error_errno(errno
, "Failed to remove policy file '%s': %m", path
);
4196 log_info("Removed policy file '%s'.", path
);
4200 static int verb_make_policy(int argc
, char *argv
[], void *userdata
) {
4203 /* Here's how this all works: after predicting all possible PCR values for next boot (with
4204 * alternatives) we'll calculate a policy from it as a combination of PolicyPCR + PolicyOR
4205 * expressions. This is then stored in an NV index. When a component of the boot process is changed a
4206 * new prediction is made and the NV index updated (which automatically invalidates any older
4209 * Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
4210 * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
4211 * policies matching the current NV index contents.
4213 * We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
4214 * either generate locally or which the user can provide us with) which can also be used for
4215 * recovery. This PIN is sealed to the TPM and is locked via PolicyAuthorizeNV to the NV index it
4216 * protects (i.e. we dogfood 🌭 🐶 hard here). This means in order to update such a policy we need
4217 * the policy to pass.
4219 * Information about the used NV Index, the SRK of the TPM, the sealed PIN and the current PCR
4220 * prediction data are stored in a JSON file in /var/lib/. In order to be able to unlock root disks
4221 * this data must be also copied to the ESP so that it is available to the initrd. The data is not
4222 * sensitive, as SRK and NV index are pinned by it, and the prediction data must match the NV index
4225 usec_t start_usec
= now(CLOCK_MONOTONIC
);
4227 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4228 r
= event_log_load_and_process(&el
);
4232 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction new_prediction
= {
4233 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4235 r
= event_log_reduce_to_safe_pcrs(el
, &new_prediction
.pcrs
);
4239 usec_t predict_start_usec
= now(CLOCK_MONOTONIC
);
4241 r
= tpm2_pcr_prediction_run(el
, &new_prediction
);
4245 log_info("Predicted future PCRs in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), predict_start_usec
), 1));
4247 _cleanup_(json_variant_unrefp
) JsonVariant
*new_prediction_json
= NULL
;
4248 r
= tpm2_pcr_prediction_to_json(&new_prediction
, el
->primary_algorithm
, &new_prediction_json
);
4253 (void) json_variant_dump(new_prediction_json
, JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
, stderr
, NULL
);
4255 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy old_policy
= {};
4257 r
= tpm2_pcrlock_policy_load(arg_pcrlock_path
, &old_policy
);
4261 bool have_old_policy
= r
> 0;
4263 /* When we update the policy the old serializations for NV, SRK, PIN remain the same */
4264 _cleanup_(iovec_done
) struct iovec
4265 nv_blob
= TAKE_STRUCT(old_policy
.nv_handle
),
4266 nv_public_blob
= TAKE_STRUCT(old_policy
.nv_public
),
4267 srk_blob
= TAKE_STRUCT(old_policy
.srk_handle
),
4268 pin_public
= TAKE_STRUCT(old_policy
.pin_public
),
4269 pin_private
= TAKE_STRUCT(old_policy
.pin_private
);
4271 if (have_old_policy
) {
4272 if (arg_nv_index
!= 0 && old_policy
.nv_index
!= arg_nv_index
)
4273 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
);
4276 old_policy
.algorithm
== el
->primary_algorithm
&&
4277 tpm2_pcr_prediction_equal(&old_policy
.prediction
, &new_prediction
, el
->primary_algorithm
)) {
4278 log_info("Prediction is identical to current policy, skipping update.");
4279 return EXIT_SUCCESS
;
4283 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4284 r
= tpm2_context_new(NULL
, &tc
);
4286 return log_error_errno(r
, "Failed to allocate TPM2 context: %m");
4288 if (!tpm2_supports_command(tc
, TPM2_CC_PolicyAuthorizeNV
))
4289 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "TPM2 does not support PolicyAuthorizeNV command, refusing.");
4291 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4293 if (iovec_is_set(&srk_blob
)) {
4294 r
= tpm2_deserialize(
4300 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4302 r
= tpm2_get_or_create_srk(
4304 /* session= */ NULL
,
4305 /* ret_public= */ NULL
,
4306 /* ret_name= */ NULL
,
4307 /* ret_qname= */ NULL
,
4310 return log_error_errno(r
, "Failed to install SRK: %m");
4313 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4314 r
= tpm2_make_encryption_session(
4317 /* bind_key= */ &TPM2_HANDLE_NONE
,
4318 &encryption_session
);
4320 return log_error_errno(r
, "Failed to allocate encryption session: %m");
4322 /* Acquire a recovery PIN, either from the user, or create a randomized one */
4323 _cleanup_(erase_and_freep
) char *pin
= NULL
;
4324 if (arg_recovery_pin
) {
4325 r
= getenv_steal_erase("PIN", &pin
);
4327 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
4329 _cleanup_(strv_free_erasep
) char **l
= NULL
;
4331 r
= ask_password_auto(
4334 /* id= */ "pcrlock-recovery-pin",
4335 /* key_name= */ NULL
,
4336 /* credential_name= */ "systemd-pcrlock.recovery-pin",
4341 return log_error_errno(r
, "Failed to query for recovery PIN: %m");
4343 if (strv_length(l
) != 1)
4344 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expected a single PIN only.");
4346 pin
= TAKE_PTR(l
[0]);
4350 } else if (!have_old_policy
) {
4353 r
= crypto_random_bytes(rnd
, sizeof(rnd
));
4355 return log_error_errno(r
, "Failed to generate a randomized recovery PIN: %m");
4357 (void) base64mem(rnd
, sizeof(rnd
), &pin
);
4358 explicit_bzero_safe(rnd
, sizeof(rnd
));
4363 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4364 TPM2_HANDLE nv_index
= 0;
4366 if (iovec_is_set(&nv_blob
)) {
4367 r
= tpm2_deserialize(tc
, nv_blob
.iov_base
, nv_blob
.iov_len
, &nv_handle
);
4369 return log_error_errno(r
, "Failed to deserialize NV index TR: %m");
4371 nv_index
= old_policy
.nv_index
;
4374 TPM2B_AUTH auth
= {};
4375 CLEANUP_ERASE(auth
);
4378 r
= tpm2_get_pin_auth(TPM2_ALG_SHA256
, pin
, &auth
);
4380 return log_error_errno(r
, "Failed to hash PIN: %m");
4382 assert(iovec_is_set(&pin_public
));
4383 assert(iovec_is_set(&pin_private
));
4385 log_debug("Retrieving PIN from sealed data.");
4387 usec_t pin_start_usec
= now(CLOCK_MONOTONIC
);
4389 _cleanup_(iovec_done_erase
) struct iovec secret
= {};
4390 for (unsigned attempt
= 0;; attempt
++) {
4391 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4393 r
= tpm2_make_policy_session(
4399 return log_error_errno(r
, "Failed to allocate policy session: %m");
4401 r
= tpm2_policy_super_pcr(
4404 &old_policy
.prediction
,
4405 old_policy
.algorithm
);
4407 return log_error_errno(r
, "Failed to submit super PCR policy: %m");
4409 r
= tpm2_policy_authorize_nv(
4415 return log_error_errno(r
, "Failed to submit AuthorizeNV policy: %m");
4417 r
= tpm2_unseal_data(
4425 if (r
< 0 && (r
!= -ESTALE
|| attempt
>= 16))
4426 return log_error_errno(r
, "Failed to unseal PIN: %m");
4430 log_debug("Trying again (attempt %u), as PCR values changed during unlock attempt.", attempt
+1);
4433 if (secret
.iov_len
> sizeof_field(TPM2B_AUTH
, buffer
))
4434 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Decrypted PIN too large.");
4436 auth
= (TPM2B_AUTH
) {
4437 .size
= secret
.iov_len
,
4440 memcpy_safe(auth
.buffer
, secret
.iov_base
, secret
.iov_len
);
4442 log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_start_usec
), 1));
4445 TPM2B_NV_PUBLIC nv_public
= {};
4447 usec_t nv_index_start_usec
= now(CLOCK_MONOTONIC
);
4449 if (!iovec_is_set(&nv_blob
)) {
4450 TPM2B_DIGEST recovery_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4451 r
= tpm2_calculate_policy_auth_value(&recovery_policy_digest
);
4453 return log_error_errno(r
, "Failed to calculate authentication value policy: %m");
4455 log_debug("Allocating NV index to write PCR policy to...");
4456 r
= tpm2_define_policy_nv_index(
4460 &recovery_policy_digest
,
4467 return log_error_errno(r
, "NV index 0x%" PRIx32
" already allocated.", arg_nv_index
);
4469 return log_error_errno(r
, "Failed to allocate NV index: %m");
4472 r
= tpm2_set_auth_binary(tc
, nv_handle
, &auth
);
4474 return log_error_errno(r
, "Failed to set authentication value on NV index: %m");
4476 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4477 r
= tpm2_make_policy_session(
4483 return log_error_errno(r
, "Failed to allocate policy session: %m");
4485 r
= tpm2_policy_auth_value(
4488 /* ret_policy_digest= */ NULL
);
4490 return log_error_errno(r
, "Failed to submit authentication value policy: %m");
4492 log_debug("Calculating new PCR policy to write...");
4493 TPM2B_DIGEST new_super_pcr_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4495 usec_t pcr_policy_start_usec
= now(CLOCK_MONOTONIC
);
4497 r
= tpm2_calculate_policy_super_pcr(
4499 el
->primary_algorithm
,
4500 &new_super_pcr_policy_digest
);
4502 return log_error_errno(r
, "Failed to calculate super PCR policy: %m");
4504 log_info("Calculated new PCR policy in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pcr_policy_start_usec
), 1));
4506 log_debug("Writing new PCR policy to NV index...");
4507 r
= tpm2_write_policy_nv_index(
4512 &new_super_pcr_policy_digest
);
4514 return log_error_errno(r
, "Failed to write to NV index: %m");
4516 log_info("Updated NV index in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), nv_index_start_usec
), 1));
4518 assert(iovec_is_set(&pin_public
) == iovec_is_set(&pin_private
));
4519 if (!iovec_is_set(&pin_public
)) {
4520 TPM2B_DIGEST authnv_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4522 r
= tpm2_calculate_policy_authorize_nv(&nv_public
, &authnv_policy_digest
);
4524 return log_error_errno(r
, "Failed to calculate AuthorizeNV policy: %m");
4526 struct iovec data
= {
4527 .iov_base
= auth
.buffer
,
4528 .iov_len
= auth
.size
,
4531 usec_t pin_seal_start_usec
= now(CLOCK_MONOTONIC
);
4533 log_debug("Sealing PIN to NV index policy...");
4539 &authnv_policy_digest
,
4543 return log_error_errno(r
, "Failed to seal PIN to NV auth policy: %m");
4545 log_info("Sealed PIN in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_seal_start_usec
), 1));
4548 if (!iovec_is_set(&nv_blob
)) {
4549 r
= tpm2_serialize(tc
, nv_handle
, &nv_blob
.iov_base
, &nv_blob
.iov_len
);
4551 return log_error_errno(r
, "Failed to serialize NV index TR: %m");
4554 if (!iovec_is_set(&srk_blob
)) {
4555 r
= tpm2_serialize(tc
, srk_handle
, &srk_blob
.iov_base
, &srk_blob
.iov_len
);
4557 return log_error_errno(r
, "Failed to serialize SRK index TR: %m");
4560 if (!iovec_is_set(&nv_public_blob
)) {
4561 r
= tpm2_marshal_nv_public(&nv_public
, &nv_public_blob
.iov_base
, &nv_public_blob
.iov_len
);
4563 return log_error_errno(r
, "Failed to marshal NV public area: %m");
4566 _cleanup_(json_variant_unrefp
) JsonVariant
*new_configuration_json
= NULL
;
4567 r
= json_build(&new_configuration_json
,
4569 JSON_BUILD_PAIR_STRING("pcrBank", tpm2_hash_alg_to_string(el
->primary_algorithm
)),
4570 JSON_BUILD_PAIR_VARIANT("pcrValues", new_prediction_json
),
4571 JSON_BUILD_PAIR_INTEGER("nvIndex", nv_index
),
4572 JSON_BUILD_PAIR_IOVEC_BASE64("nvHandle", &nv_blob
),
4573 JSON_BUILD_PAIR_IOVEC_BASE64("nvPublic", &nv_public_blob
),
4574 JSON_BUILD_PAIR_IOVEC_BASE64("srkHandle", &srk_blob
),
4575 JSON_BUILD_PAIR_IOVEC_BASE64("pinPublic", &pin_public
),
4576 JSON_BUILD_PAIR_IOVEC_BASE64("pinPrivate", &pin_private
)));
4578 return log_error_errno(r
, "Failed to generate JSON: %m");
4580 _cleanup_free_
char *text
= NULL
;
4581 r
= json_variant_format(new_configuration_json
, 0, &text
);
4583 return log_error_errno(r
, "Failed to format new configuration to JSON: %m");
4585 const char *path
= arg_pcrlock_path
?: (in_initrd() ? "/run/systemd/pcrlock.json" : "/var/lib/systemd/pcrlock.json");
4586 r
= write_string_file(path
, text
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_SYNC
|WRITE_STRING_FILE_MKDIR_0755
);
4588 return log_error_errno(r
, "Failed to write new configuration to '%s': %m", path
);
4590 if (!arg_pcrlock_path
&& !in_initrd()) {
4591 r
= remove_policy_file("/run/systemd/pcrlock.json");
4596 log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%" PRIu32
".", path
, nv_index
);
4598 log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), start_usec
), 1));
4603 static int undefine_policy_nv_index(
4605 const struct iovec
*nv_blob
,
4606 const struct iovec
*srk_blob
) {
4612 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4613 r
= tpm2_context_new(NULL
, &tc
);
4617 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4618 r
= tpm2_deserialize(
4624 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4626 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4627 r
= tpm2_deserialize(
4633 return log_error_errno(r
, "Failed to deserialize NV TR: %m");
4635 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4636 r
= tpm2_make_encryption_session(
4639 /* bind_key= */ &TPM2_HANDLE_NONE
,
4640 &encryption_session
);
4644 r
= tpm2_undefine_policy_nv_index(
4652 log_info("Removed NV index 0x%x", nv_index
);
4656 static int verb_remove_policy(int argc
, char *argv
[], void *userdata
) {
4659 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy policy
= {};
4660 r
= tpm2_pcrlock_policy_load(arg_policy_path
, &policy
);
4662 log_info("No policy found.");
4667 log_notice("Failed to load old policy file, assuming it is corrupted, removing.");
4669 r
= undefine_policy_nv_index(policy
.nv_index
, &policy
.nv_handle
, &policy
.srk_handle
);
4671 log_notice("Failed to remove NV index, assuming data out of date, removing policy file.");
4674 if (arg_policy_path
) {
4675 r
= remove_policy_file(arg_policy_path
);
4683 RET_GATHER(ret
, remove_policy_file("/var/lib/systemd/pcrlock.json"));
4684 RET_GATHER(ret
, remove_policy_file("/run/systemd/pcrlock.json"));
4690 static int help(int argc
, char *argv
[], void *userdata
) {
4691 _cleanup_free_
char *link
= NULL
;
4694 r
= terminal_urlify_man("systemd-pcrlock", "8", &link
);
4698 printf("%1$s [OPTIONS...] COMMAND ...\n"
4699 "\n%5$sManage a TPM2 PCR lock.%6$s\n"
4700 "\n%3$sCommands:%4$s\n"
4701 " log Show measurement log\n"
4702 " cel Show measurement log in TCG CEL-JSON format\n"
4703 " list-components List defined .pcrlock components\n"
4704 " predict Predict PCR values\n"
4705 " make-policy Predict PCR values and generate TPM2 policy from it\n"
4706 " remove-policy Remove TPM2 policy\n"
4707 "\n%3$sProtections:%4$s\n"
4708 " lock-firmware-code Generate a .pcrlock file from current firmware code\n"
4709 " unlock-firmware-code Remove .pcrlock file for firmware code\n"
4710 " lock-firmware-config Generate a .pcrlock file from current firmware configuration\n"
4711 " unlock-firmware-config Remove .pcrlock file for firmware configuration\n"
4712 " lock-secureboot-policy Generate a .pcrlock file from current SecureBoot policy\n"
4713 " unlock-secureboot-policy Remove .pcrlock file for SecureBoot policy\n"
4714 " lock-secureboot-authority Generate a .pcrlock file from current SecureBoot authority\n"
4715 " unlock-secureboot-authority Remove .pcrlock file for SecureBoot authority\n"
4716 " lock-gpt [DISK] Generate a .pcrlock file from GPT header\n"
4717 " unlock-gpt Remove .pcrlock file for GPT header\n"
4718 " lock-pe [BINARY] Generate a .pcrlock file from PE binary\n"
4719 " unlock-pe Remove .pcrlock file for PE binary\n"
4720 " lock-uki [UKI] Generate a .pcrlock file from UKI PE binary\n"
4721 " unlock-uki Remove .pcrlock file for UKI PE binary\n"
4722 " lock-machine-id Generate a .pcrlock file from current machine ID\n"
4723 " unlock-machine-id Remove .pcrlock file for machine ID\n"
4724 " lock-file-system [PATH] Generate a .pcrlock file from current root fs + /var/\n"
4725 " unlock-file-system [PATH] Remove .pcrlock file for root fs + /var/\n"
4726 " lock-kernel-cmdline [FILE] Generate a .pcrlock file from kernel command line\n"
4727 " unlock-kernel-cmdline Remove .pcrlock file for kernel command line\n"
4728 " lock-kernel-initrd FILE Generate a .pcrlock file from an initrd file\n"
4729 " unlock-kernel-initrd Remove .pcrlock file for an initrd file\n"
4730 " lock-raw [FILE] Generate a .pcrlock file from raw data\n"
4731 " unlock-raw Remove .pcrlock file for raw data\n"
4732 "\n%3$sOptions:%4$s\n"
4733 " -h --help Show this help\n"
4734 " --version Print version\n"
4735 " --no-pager Do not pipe output into a pager\n"
4736 " --json=pretty|short|off Generate JSON output\n"
4737 " --raw-description Show raw firmware record data as description in table\n"
4738 " --pcr=NR Generate .pcrlock for specified PCR\n"
4739 " --nv-index=NUMBER Use the specified NV index, instead of a random one\n"
4740 " --components=PATH Directory to read .pcrlock files from\n"
4741 " --location=STRING[:STRING]\n"
4742 " Do not process components beyond this component name\n"
4743 " --recovery-pin=yes Ask for a recovery PIN\n"
4744 " --pcrlock=PATH .pcrlock file to write expected PCR measurement to\n"
4745 " --policy=PATH JSON file to write policy output to\n"
4746 " --force Write policy even if it matches existing policy\n"
4747 "\nSee the %2$s for details.\n",
4748 program_invocation_short_name
,
4758 static int parse_argv(int argc
, char *argv
[]) {
4760 ARG_VERSION
= 0x100,
4763 ARG_RAW_DESCRIPTION
,
4774 static const struct option options
[] = {
4775 { "help", no_argument
, NULL
, 'h' },
4776 { "version", no_argument
, NULL
, ARG_VERSION
},
4777 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
4778 { "json", required_argument
, NULL
, ARG_JSON
},
4779 { "raw-description", no_argument
, NULL
, ARG_RAW_DESCRIPTION
},
4780 { "pcr", required_argument
, NULL
, ARG_PCR
},
4781 { "nv-index", required_argument
, NULL
, ARG_NV_INDEX
},
4782 { "components", required_argument
, NULL
, ARG_COMPONENTS
},
4783 { "location", required_argument
, NULL
, ARG_LOCATION
},
4784 { "recovery-pin", required_argument
, NULL
, ARG_RECOVERY_PIN
},
4785 { "pcrlock", required_argument
, NULL
, ARG_PCRLOCK
},
4786 { "policy", required_argument
, NULL
, ARG_POLICY
},
4787 { "force", no_argument
, NULL
, ARG_FORCE
},
4791 bool auto_location
= true;
4797 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
4801 help(0, NULL
, NULL
);
4808 arg_pager_flags
|= PAGER_DISABLE
;
4812 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4817 case ARG_RAW_DESCRIPTION
:
4818 arg_raw_description
= true;
4822 r
= tpm2_parse_pcr_argument_to_mask(optarg
, &arg_pcr_mask
);
4824 return log_error_errno(r
, "Failed to parse PCR specification: %s", optarg
);
4830 if (isempty(optarg
))
4835 r
= safe_atou32_full(optarg
, 16, &u
);
4837 return log_error_errno(r
, "Failed to parse --nv-index= argument: %s", optarg
);
4839 if (u
< TPM2_NV_INDEX_FIRST
|| u
> TPM2_NV_INDEX_LAST
)
4840 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument for --nv-index= outside of valid range 0x%" PRIx32
"…0x%" PRIx32
": 0x%" PRIx32
,
4841 TPM2_NV_INDEX_FIRST
, TPM2_NV_INDEX_LAST
, u
);
4847 case ARG_COMPONENTS
: {
4848 _cleanup_free_
char *p
= NULL
;
4850 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &p
);
4854 r
= strv_consume(&arg_components
, TAKE_PTR(p
));
4861 case ARG_LOCATION
: {
4862 _cleanup_free_
char *start
= NULL
, *end
= NULL
;
4865 auto_location
= false;
4867 if (isempty(optarg
)) {
4868 arg_location_start
= mfree(arg_location_start
);
4869 arg_location_end
= mfree(arg_location_end
);
4873 e
= strchr(optarg
, ':');
4875 start
= strndup(optarg
, e
- optarg
);
4879 end
= strdup(e
+ 1);
4883 start
= strdup(optarg
);
4887 end
= strdup(optarg
);
4892 if (!filename_is_valid(start
))
4893 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", start
);
4894 if (!filename_is_valid(end
))
4895 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", end
);
4897 free_and_replace(arg_location_start
, start
);
4898 free_and_replace(arg_location_end
, end
);
4902 case ARG_RECOVERY_PIN
:
4903 r
= parse_boolean_argument("--recovery-pin", optarg
, &arg_recovery_pin
);
4909 if (empty_or_dash(optarg
))
4910 arg_pcrlock_path
= mfree(arg_pcrlock_path
);
4912 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_pcrlock_path
);
4917 arg_pcrlock_auto
= false;
4921 if (empty_or_dash(optarg
))
4922 arg_policy_path
= mfree(arg_policy_path
);
4924 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_policy_path
);
4939 assert_not_reached();
4942 if (auto_location
) {
4943 assert(!arg_location_start
);
4944 assert(!arg_location_end
);
4946 arg_location_start
= strdup("760-");
4947 if (!arg_location_start
)
4950 arg_location_end
= strdup("940-");
4951 if (!arg_location_end
)
4958 static int pcrlock_main(int argc
, char *argv
[]) {
4959 static const Verb verbs
[] = {
4960 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
4961 { "log", VERB_ANY
, 1, VERB_DEFAULT
, verb_show_log
},
4962 { "cel", VERB_ANY
, 1, 0, verb_show_cel
},
4963 { "list-components", VERB_ANY
, 1, 0, verb_list_components
},
4964 { "predict", VERB_ANY
, 1, 0, verb_predict
},
4965 { "lock-firmware-code", VERB_ANY
, 2, 0, verb_lock_firmware
},
4966 { "unlock-firmware-code", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4967 { "lock-firmware-config", VERB_ANY
, 2, 0, verb_lock_firmware
},
4968 { "unlock-firmware-config", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4969 { "lock-secureboot-policy", VERB_ANY
, 1, 0, verb_lock_secureboot_policy
},
4970 { "unlock-secureboot-policy", VERB_ANY
, 1, 0, verb_unlock_secureboot_policy
},
4971 { "lock-secureboot-authority", VERB_ANY
, 1, 0, verb_lock_secureboot_authority
},
4972 { "unlock-secureboot-authority", VERB_ANY
, 1, 0, verb_unlock_secureboot_authority
},
4973 { "lock-gpt", VERB_ANY
, 2, 0, verb_lock_gpt
},
4974 { "unlock-gpt", VERB_ANY
, 1, 0, verb_unlock_gpt
},
4975 { "lock-pe", VERB_ANY
, 2, 0, verb_lock_pe
},
4976 { "unlock-pe", VERB_ANY
, 1, 0, verb_unlock_simple
},
4977 { "lock-uki", VERB_ANY
, 2, 0, verb_lock_uki
},
4978 { "unlock-uki", VERB_ANY
, 1, 0, verb_unlock_simple
},
4979 { "lock-machine-id", VERB_ANY
, 1, 0, verb_lock_machine_id
},
4980 { "unlock-machine-id", VERB_ANY
, 1, 0, verb_unlock_machine_id
},
4981 { "lock-file-system", VERB_ANY
, 2, 0, verb_lock_file_system
},
4982 { "unlock-file-system", VERB_ANY
, 2, 0, verb_unlock_file_system
},
4983 { "lock-kernel-cmdline", VERB_ANY
, 2, 0, verb_lock_kernel_cmdline
},
4984 { "unlock-kernel-cmdline", VERB_ANY
, 1, 0, verb_unlock_kernel_cmdline
},
4985 { "lock-kernel-initrd", VERB_ANY
, 2, 0, verb_lock_kernel_initrd
},
4986 { "unlock-kernel-initrd", VERB_ANY
, 1, 0, verb_unlock_kernel_initrd
},
4987 { "lock-raw", VERB_ANY
, 2, 0, verb_lock_raw
},
4988 { "unlock-raw", VERB_ANY
, 1, 0, verb_unlock_simple
},
4989 { "make-policy", VERB_ANY
, 1, 0, verb_make_policy
},
4990 { "remove-policy", VERB_ANY
, 1, 0, verb_remove_policy
},
4994 return dispatch_verb(argc
, argv
, verbs
, NULL
);
4997 static int run(int argc
, char *argv
[]) {
5000 log_show_color(true);
5001 log_parse_environment();
5004 r
= parse_argv(argc
, argv
);
5008 return pcrlock_main(argc
, argv
);
5011 DEFINE_MAIN_FUNCTION(run
);