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 "color-util.h"
15 #include "conf-files.h"
21 #include "format-table.h"
22 #include "format-util.h"
25 #include "hash-funcs.h"
26 #include "hexdecoct.h"
27 #include "initrd-util.h"
28 #include "main-func.h"
29 #include "mkdir-label.h"
30 #include "openssl-util.h"
31 #include "ordered-set.h"
32 #include "parse-argument.h"
33 #include "parse-util.h"
34 #include "path-util.h"
35 #include "pcrextend-util.h"
36 #include "pcrlock-firmware.h"
38 #include "pretty-print.h"
39 #include "proc-cmdline.h"
40 #include "random-util.h"
41 #include "recovery-key.h"
42 #include "sort-util.h"
43 #include "terminal-util.h"
44 #include "tpm2-util.h"
45 #include "unaligned.h"
46 #include "unit-name.h"
50 static PagerFlags arg_pager_flags
= 0;
51 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
|JSON_FORMAT_NEWLINE
;
52 static char **arg_components
= NULL
;
53 static uint32_t arg_pcr_mask
= 0;
54 static char *arg_pcrlock_path
= NULL
;
55 static bool arg_pcrlock_auto
= true;
56 static bool arg_raw_description
= false;
57 static char *arg_location_start
= NULL
;
58 static char *arg_location_end
= NULL
;
59 static TPM2_HANDLE arg_nv_index
= 0;
60 static bool arg_recovery_pin
= false;
61 static char *arg_policy_path
= NULL
;
62 static bool arg_force
= false;
64 STATIC_DESTRUCTOR_REGISTER(arg_components
, strv_freep
);
65 STATIC_DESTRUCTOR_REGISTER(arg_pcrlock_path
, freep
);
66 STATIC_DESTRUCTOR_REGISTER(arg_location_start
, freep
);
67 STATIC_DESTRUCTOR_REGISTER(arg_location_end
, freep
);
68 STATIC_DESTRUCTOR_REGISTER(arg_policy_path
, freep
);
70 #define PCRLOCK_SECUREBOOT_POLICY_PATH "/var/lib/pcrlock.d/240-secureboot-policy.pcrlock.d/generated.pcrlock"
71 #define PCRLOCK_FIRMWARE_CODE_EARLY_PATH "/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock"
72 #define PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH "/var/lib/pcrlock.d/250-firmware-config-early.pcrlock.d/generated.pcrlock"
73 #define PCRLOCK_FIRMWARE_CODE_LATE_PATH "/var/lib/pcrlock.d/550-firmware-code-late.pcrlock.d/generated.pcrlock"
74 #define PCRLOCK_FIRMWARE_CONFIG_LATE_PATH "/var/lib/pcrlock.d/550-firmware-config-late.pcrlock.d/generated.pcrlock"
75 #define PCRLOCK_GPT_PATH "/var/lib/pcrlock.d/600-gpt.pcrlock.d/generated.pcrlock"
76 #define PCRLOCK_SECUREBOOT_AUTHORITY_PATH "/var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock"
77 #define PCRLOCK_KERNEL_CMDLINE_PATH "/var/lib/pcrlock.d/710-kernel-cmdline.pcrlock/generated.pcrlock"
78 #define PCRLOCK_KERNEL_INITRD_PATH "/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/generated.pcrlock"
79 #define PCRLOCK_MACHINE_ID_PATH "/var/lib/pcrlock.d/820-machine-id.pcrlock"
80 #define PCRLOCK_ROOT_FILE_SYSTEM_PATH "/var/lib/pcrlock.d/830-root-file-system.pcrlock"
81 #define PCRLOCK_FILE_SYSTEM_PATH_PREFIX "/var/lib/pcrlock.d/840-file-system-"
83 /* The default set of PCRs to lock to */
84 #define DEFAULT_PCR_MASK \
85 ((UINT32_C(1) << TPM2_PCR_PLATFORM_CODE) | \
86 (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG) | \
87 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE) | \
88 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG) | \
89 (UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE) | \
90 (UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG) | \
91 (UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY) | \
92 (UINT32_C(1) << TPM2_PCR_KERNEL_BOOT) | \
93 (UINT32_C(1) << TPM2_PCR_KERNEL_CONFIG) | \
94 (UINT32_C(1) << TPM2_PCR_SYSEXTS) | \
95 (UINT32_C(1) << TPM2_PCR_SHIM_POLICY) | \
96 (UINT32_C(1) << TPM2_PCR_SYSTEM_IDENTITY))
98 typedef struct EventLogRecordBank EventLogRecordBank
;
99 typedef struct EventLogRecord EventLogRecord
;
100 typedef struct EventLogRegisterBank EventLogRegisterBank
;
101 typedef struct EventLogRegister EventLogRegister
;
102 typedef struct EventLogComponentVariant EventLogComponentVariant
;
103 typedef struct EventLogComponent EventLogComponent
;
104 typedef struct EventLog EventLog
;
106 struct EventLogRecordBank
{
109 LIST_FIELDS(EventLogRecordBank
, banks
);
112 typedef enum EventPayloadValid
{
113 EVENT_PAYLOAD_VALID_YES
,
114 EVENT_PAYLOAD_VALID_NO
,
115 EVENT_PAYLOAD_VALID_DONT_KNOW
,
116 _EVENT_PAYLOAD_VALID_MAX
,
117 _EVENT_PAYLOAD_VALID_INVALID
= -EINVAL
,
120 struct EventLogRecord
{
127 /* Data for firmware events (i.e. "TCG PC Client Platform Firmware Profile Specification" events) */
128 uint32_t firmware_event_type
;
129 void *firmware_payload
;
130 size_t firmware_payload_size
;
132 /* Data for userspace events (i.e. those generated by systemd in userspace */
133 Tpm2UserspaceEventType userspace_event_type
;
134 JsonVariant
*userspace_content
;
136 /* Validation result for the event payload itself, if the record contains enough information to validate the hash */
137 EventPayloadValid event_payload_valid
;
139 /* If this record matches an variant of one of our defined components */
140 EventLogComponentVariant
**mapped
;
143 /* If this record is part of an EventLogComponentVariant */
144 EventLogComponentVariant
*owning_component_variant
;
146 LIST_HEAD(EventLogRecordBank
, banks
);
149 #define EVENT_LOG_RECORD_IS_FIRMWARE(record) ((record)->firmware_event_type != UINT32_MAX)
150 #define EVENT_LOG_RECORD_IS_USERSPACE(record) ((record)->userspace_event_type >= 0)
152 struct EventLogRegisterBank
{
153 TPM2B_DIGEST observed
;
154 TPM2B_DIGEST calculated
;
157 struct EventLogRegister
{
159 unsigned n_measurements
;
160 bool fully_recognized
; /* true if all measurements in this register have been recognized to match components */
161 EventLogRegisterBank
*banks
;
164 struct EventLogComponentVariant
{
165 EventLogComponent
*component
;
170 EventLogRecord
**records
;
174 struct EventLogComponent
{
177 EventLogComponentVariant
**variants
;
182 EventLogRecord
**records
;
185 uint16_t *algorithms
;
187 bool algorithms_locked
; /* if algorithms where set explicitly by user, and we should not determine them automatically */
191 /* The hash algorithm which we focus on for matching up components */
192 uint16_t primary_algorithm
;
194 uint8_t startup_locality
;
195 bool startup_locality_found
;
197 EventLogRegister registers
[TPM2_PCRS_MAX
];
199 EventLogComponent
**components
;
202 /* Number of components which we couldn't find in the event log */
203 size_t n_missing_components
;
205 /* PCRs mask indicating all PCRs touched by unrecognized components */
206 uint32_t missing_component_pcrs
;
209 static EventLogRecordBank
*event_log_record_bank_free(EventLogRecordBank
*bank
) {
213 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogRecordBank
*, event_log_record_bank_free
);
215 static EventLogRecord
*event_log_record_free(EventLogRecord
*record
) {
216 EventLogRecordBank
*bank
;
221 free(record
->description
);
222 free(record
->firmware_payload
);
223 json_variant_unref(record
->userspace_content
);
225 while ((bank
= LIST_POP(banks
, record
->banks
)))
226 event_log_record_bank_free(bank
);
228 free(record
->mapped
);
230 return mfree(record
);
233 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogRecord
*, event_log_record_free
);
235 static void event_log_register_done(EventLog
*el
, EventLogRegister
*reg
) {
242 static EventLogComponentVariant
* event_log_component_variant_free(EventLogComponentVariant
*variant
) {
249 FOREACH_ARRAY(record
, variant
->records
, variant
->n_records
)
250 event_log_record_free(*record
);
252 free(variant
->records
);
254 return mfree(variant
);
257 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogComponentVariant
*, event_log_component_variant_free
);
259 static EventLogComponent
* event_log_component_free(EventLogComponent
*component
) {
263 FOREACH_ARRAY(variant
, component
->variants
, component
->n_variants
)
264 event_log_component_variant_free(*variant
);
265 free(component
->variants
);
269 return mfree(component
);
272 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLogComponent
*, event_log_component_free
);
274 static EventLog
* event_log_free(EventLog
*el
) {
278 FOREACH_ARRAY(p
, el
->registers
, TPM2_PCRS_MAX
)
279 event_log_register_done(el
, p
);
281 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
)
282 event_log_record_free(*rr
);
285 FOREACH_ARRAY(c
, el
->components
, el
->n_components
)
286 event_log_component_free(*c
);
287 free(el
->components
);
289 free(el
->algorithms
);
295 DEFINE_TRIVIAL_CLEANUP_FUNC(EventLog
*, event_log_free
);
297 static EventLogRecord
* event_log_record_new(EventLog
*el
) {
298 EventLogRecord
*record
;
300 record
= new(EventLogRecord
, 1);
304 *record
= (EventLogRecord
) {
306 .firmware_event_type
= UINT32_MAX
,
307 .userspace_event_type
= _TPM2_USERSPACE_EVENT_TYPE_INVALID
,
308 .event_payload_valid
= _EVENT_PAYLOAD_VALID_INVALID
,
314 static int event_log_add_record(
316 EventLogRecord
**ret
) {
318 _cleanup_(event_log_record_freep
) EventLogRecord
*record
= NULL
;
322 if (!GREEDY_REALLOC(el
->records
, el
->n_records
+1))
325 record
= event_log_record_new(el
);
329 el
->records
[el
->n_records
++] = record
;
339 static int event_log_add_algorithm(EventLog
*el
, uint16_t alg
) {
342 if (el
->algorithms_locked
) /* algorithms configured via env var, don't add any further automatically */
345 if (typesafe_bsearch(&alg
, el
->algorithms
, el
->n_algorithms
, cmp_uint16
))
348 if (!GREEDY_REALLOC(el
->algorithms
, el
->n_algorithms
+1))
351 el
->algorithms
[el
->n_algorithms
++] = alg
;
353 typesafe_qsort(el
->algorithms
, el
->n_algorithms
, cmp_uint16
);
358 static int event_log_add_algorithms_from_environment(EventLog
*el
) {
364 e
= secure_getenv("SYSTEMD_TPM2_HASH_ALGORITHMS");
369 _cleanup_free_
char *word
= NULL
;
371 r
= extract_first_word(&e
, &word
, ":", 0);
377 r
= tpm2_hash_alg_from_string(word
);
379 return log_error_errno(r
, "Unknown hash algorithm '%s'.", word
);
381 r
= event_log_add_algorithm(el
, r
);
383 return log_error_errno(r
, "Failed to add hash algorithm '%s'.", word
);
386 if (el
->n_algorithms
> 0)
387 el
->algorithms_locked
= true;
392 static EventLogRecordBank
*event_log_record_find_bank(
393 const EventLogRecord
*record
,
398 LIST_FOREACH(banks
, i
, record
->banks
)
399 if (i
->algorithm
== alg
)
405 static int event_log_record_add_bank(
406 EventLogRecord
*record
,
410 EventLogRecordBank
**ret
) {
412 _cleanup_(event_log_record_bank_freep
) EventLogRecordBank
*bank
= NULL
;
413 _cleanup_free_
void *h
= NULL
;
416 assert(hash
|| hash_size
== 0);
418 if (event_log_record_find_bank(record
, algorithm
))
421 if (hash_size
> sizeof_field(TPM2B_DIGEST
, buffer
))
424 h
= memdup(hash
, hash_size
);
428 bank
= new(EventLogRecordBank
, 1);
432 *bank
= (EventLogRecordBank
) {
433 .algorithm
= algorithm
,
434 .hash
= TPM2B_DIGEST_MAKE(hash
, hash_size
),
437 LIST_PREPEND(banks
, record
->banks
, bank
);
447 static bool event_log_record_is_stub(EventLogRecord
*rec
) {
450 /* Recognizes the special EV_IPL events systemd-stub generates. Since EV_IPL can be used by almost
451 * anything, we'll check for the PCR values, to see if it's one of ours. */
453 if (rec
->firmware_event_type
!= EV_IPL
)
456 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
459 if (!IN_SET(rec
->pcr
,
460 TPM2_PCR_KERNEL_BOOT
, /* 11 */
461 TPM2_PCR_KERNEL_CONFIG
, /* 12 */
462 TPM2_PCR_SYSEXTS
)) /* 13 */
468 static int event_log_record_parse_variable_data(
470 sd_id128_t
*ret_variable_uuid
,
471 char **ret_variable_name
) {
473 _cleanup_free_ char16_t
*p16
= NULL
;
474 _cleanup_free_
char *p
= NULL
;
477 assert(ret_variable_uuid
);
478 assert(ret_variable_name
);
480 if (rec
->firmware_payload_size
< sizeof(UEFI_VARIABLE_DATA
))
481 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI variable field too short.");
483 const UEFI_VARIABLE_DATA
*vdata
= rec
->firmware_payload
;
485 if (vdata
->unicodeNameLength
> (SIZE_MAX
- offsetof(UEFI_VARIABLE_DATA
, unicodeNameLength
)) / 2)
486 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unicode name length too large.");
488 size_t m
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + vdata
->unicodeNameLength
* 2;
490 if (vdata
->variableDataLength
> SIZE_MAX
- m
)
491 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Oversize EFI variable data size.");
493 if (rec
->firmware_payload_size
!= m
+ vdata
->variableDataLength
)
494 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI variable data has wrong size.");
496 p16
= memdup(vdata
->unicodeName
, vdata
->unicodeNameLength
* 2); /* Copy out, to align properly */
498 return log_oom_debug();
500 p
= utf16_to_utf8(p16
, vdata
->unicodeNameLength
* 2);
502 return log_oom_debug();
504 if (!string_is_safe(p
))
505 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI variable string in record.");
507 *ret_variable_uuid
= efi_guid_to_id128(vdata
->variableName
);
508 *ret_variable_name
= TAKE_PTR(p
);
513 static int event_log_record_extract_firmware_description(EventLogRecord
*rec
) {
514 _cleanup_free_
char *fallback
= NULL
;
519 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
522 if (arg_raw_description
)
525 switch (rec
->firmware_event_type
) {
527 case EV_EFI_VARIABLE_DRIVER_CONFIG
:
528 case EV_EFI_VARIABLE_BOOT
:
529 case EV_EFI_VARIABLE_BOOT2
:
530 case EV_EFI_VARIABLE_AUTHORITY
: {
531 _cleanup_free_
char *p
= NULL
;
534 r
= event_log_record_parse_variable_data(rec
, &uuid
, &p
);
538 log_warning_errno(r
, "EFI variable data invalid, ignoring.");
542 if (asprintf(&rec
->description
, "%s: %s-" SD_ID128_UUID_FORMAT_STR
,
543 rec
->firmware_event_type
== EV_EFI_VARIABLE_AUTHORITY
? "Authority" : "Variable",
545 SD_ID128_FORMAT_VAL(uuid
)) < 0)
552 if (rec
->firmware_payload_size
!= sizeof(uint32_t)) {
553 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "EFI separator field has wrong size, ignoring.");
557 uint32_t val
= unaligned_read_ne32(rec
->firmware_payload
);
562 case UINT32_C(0xffffffff):
563 (void) asprintf(&rec
->description
, "Separator: Success (0x%02" PRIx32
")", val
);
567 rec
->description
= strdup("Separator: Error (0x01)");
571 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unexpected separator payload %" PRIu32
".", val
);
575 if (!rec
->description
)
581 case EV_EFI_ACTION
: {
582 _cleanup_free_
char *d
= NULL
;
584 r
= make_cstring(rec
->firmware_payload
, rec
->firmware_payload_size
, MAKE_CSTRING_ALLOW_TRAILING_NUL
, &d
);
586 return log_error_errno(r
, "Failed to make C string from EFI action string: %m");
588 if (!string_is_safe(d
)) {
589 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI action string in record, ignoring.");
593 rec
->description
= strjoin("Action: ", d
);
594 if (!rec
->description
)
599 case EV_EFI_GPT_EVENT
: {
600 if (rec
->firmware_payload_size
< sizeof(GptHeader
)) {
601 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "GPT measurement too short, ignoring.");
605 const GptHeader
*h
= rec
->firmware_payload
;
607 if (!gpt_header_has_signature(h
)) {
608 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "GPT measurement does not cover a GPT partition table header, ignoring.");
612 if (asprintf(&rec
->description
, "GPT: disk " SD_ID128_UUID_FORMAT_STR
, SD_ID128_FORMAT_VAL(efi_guid_to_id128(h
->disk_guid
))) < 0)
619 _cleanup_free_
char *d
= NULL
;
621 /* EV_IPL can be anything, only try to parse the description on PCRs we "own" */
622 if (!event_log_record_is_stub(rec
))
625 /* sd-stub always sets a description string as text for these */
627 d
= utf16_to_utf8(rec
->firmware_payload
, rec
->firmware_payload_size
);
631 if (string_has_cc(d
, NULL
)) {
632 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Unsafe EFI action string in record, ignoring.");
636 rec
->description
= strjoin("String: ", d
);
637 if (!rec
->description
)
644 TCG_PCClientTaggedEvent
*tag
= rec
->firmware_payload
;
645 size_t left
= rec
->firmware_payload_size
;
648 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Empty tagged PC client event, ignoring.");
655 if (left
< offsetof(TCG_PCClientTaggedEvent
, taggedEventData
)) {
656 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Tagged PC client event too short, ignoring.");
660 m
= offsetof(TCG_PCClientTaggedEvent
, taggedEventData
) + (uint64_t) tag
->taggedEventDataSize
;
662 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Tagged PC client event data too short, ignoring.");
666 switch (tag
->taggedEventID
) {
668 /* Linux kernel's own measurements: */
669 case INITRD_EVENT_TAG_ID
:
670 /* The tagged event payload is just a constant string, hence don't show it */
671 if (!strextend_with_separator(&rec
->description
, ", ", "Linux: initrd"))
675 case LOAD_OPTIONS_EVENT_TAG_ID
:
677 if (!strextend_with_separator(&rec
->description
, ", ", "Linux: kernel command line"))
681 /* systemd's measurements: */
682 case LOADER_CONF_EVENT_TAG_ID
:
684 if (!strextend_with_separator(&rec
->description
, ", ", "systemd-boot: loader.conf"))
688 case DEVICETREE_ADDON_EVENT_TAG_ID
: {
689 _cleanup_free_
char *raw
= NULL
, *s
= NULL
;
691 raw
= utf16_to_utf8((const char16_t
*) tag
->taggedEventData
, tag
->taggedEventDataSize
);
699 r
= strextendf_with_separator(&rec
->description
, ", ", "systemd-stub: devicetree addon %s", s
);
701 return log_error_errno(r
, "Failed to format EV_EVENT_TAG description string: %m");
706 _cleanup_free_
char *s
= NULL
;
708 s
= cescape_length((char*) tag
->taggedEventData
, tag
->taggedEventDataSize
);
712 r
= strextendf_with_separator(&rec
->description
, ", ", "Tag 0x%" PRIx32
": %s", tag
->taggedEventID
, s
);
714 return log_error_errno(r
, "Failed to format EV_EVENT_TAG description string: %m");
719 tag
= (TCG_PCClientTaggedEvent
*) ((uint8_t*) tag
+ m
);
729 case EV_EFI_PLATFORM_FIRMWARE_BLOB
: {
730 const UEFI_PLATFORM_FIRMWARE_BLOB
*blob
;
731 if (rec
->firmware_payload_size
!= sizeof(UEFI_PLATFORM_FIRMWARE_BLOB
)) {
732 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "EV_EFI_PLATFORM_FIRMWARE_BLOB of wrong size, ignoring.");
736 blob
= rec
->firmware_payload
;
737 if (asprintf(&rec
->description
, "Blob: %s @ 0x%" PRIx64
, FORMAT_BYTES(blob
->blobLength
), blob
->blobBase
) < 0)
743 case EV_EFI_BOOT_SERVICES_APPLICATION
:
744 case EV_EFI_BOOT_SERVICES_DRIVER
:
745 case EV_EFI_RUNTIME_SERVICES_DRIVER
: {
746 const UEFI_IMAGE_LOAD_EVENT
*load
;
747 _cleanup_free_
char *fn
= NULL
;
750 if (rec
->firmware_payload_size
< offsetof(UEFI_IMAGE_LOAD_EVENT
, devicePath
)) {
751 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path too short, ignoring.");
755 load
= rec
->firmware_payload
;
756 if (load
->lengthOfDevicePath
!=
757 rec
->firmware_payload_size
- offsetof(UEFI_IMAGE_LOAD_EVENT
, devicePath
)) {
758 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path size does not match, ignoring.");
762 const packed_EFI_DEVICE_PATH
*dp
= (const packed_EFI_DEVICE_PATH
*) load
->devicePath
;
763 size_t left
= load
->lengthOfDevicePath
;
768 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Garbage after device path end, ignoring.");
776 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Garbage after device path end, ignoring.");
780 if (left
< offsetof(packed_EFI_DEVICE_PATH
, path
) || left
< dp
->length
) {
781 log_warning_errno(SYNTHETIC_ERRNO(EBADMSG
), "Device path element too short, ignoring.");
785 if (dp
->type
== 4 && dp
->subType
== 4) {
786 /* Filename, store the last node of this type as description, it should contain the file name */
789 fn
= utf16_to_utf8((void*) dp
->path
, dp
->length
- offsetof(packed_EFI_DEVICE_PATH
, path
));
793 } else if (dp
->type
== 0x7F && dp
->subType
== 0xFF)
794 /* End of Hardware Device Path */
797 log_debug("Ignoring device path element type=0x%02x subtype=0x%02x", dp
->type
, dp
->subType
);
800 dp
= (packed_EFI_DEVICE_PATH
*) ((uint8_t*) dp
+ dp
->length
);
804 rec
->description
= strjoin("File: ", fn
);
805 if (!rec
->description
)
815 /* Catchall: show binary data */
816 fallback
= cescape_length(rec
->firmware_payload
, rec
->firmware_payload_size
);
820 rec
->description
= strjoin("Raw: ", fallback
);
821 if (!rec
->description
)
827 /* Mark the payload as invalid, so that we do not bother parsing/validating it any further */
828 rec
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
832 static int event_log_add_algorithms_from_record(EventLog
*el
, EventLogRecord
*record
) {
838 if (el
->algorithms_locked
)
841 LIST_FOREACH(banks
, i
, record
->banks
) {
842 r
= event_log_add_algorithm(el
, i
->algorithm
);
850 static int event_log_load_firmware(EventLog
*el
) {
851 const TCG_EfiSpecIdEventAlgorithmSize
*algorithms
;
852 size_t bufsize
= 0, n_algorithms
= 0, left
= 0;
853 _cleanup_free_
void *buf
= NULL
;
854 const TCG_PCR_EVENT2
*event
;
860 path
= tpm2_firmware_log_path();
862 r
= read_full_file(path
, (char**) &buf
, &bufsize
);
864 return log_error_errno(r
, "Failed to open TPM2 event log '%s': %m", path
);
867 /* Sometimes it's useful to invoke things with SYSTEMD_MEASURE_LOG_FIRMWARE=/dev/null, let's allow that, and proceed */
868 log_warning("Empty firmware event log file, not loading.");
872 r
= validate_firmware_header(buf
, bufsize
, &algorithms
, &n_algorithms
, &event
, &left
);
876 for (const TCG_PCR_EVENT2
*next_event
= NULL
;; event
= next_event
) {
877 EventLogRecord
*record
= NULL
;
881 r
= validate_firmware_event(
895 if (event
->eventType
== EV_NO_ACTION
&&
896 event
->pcrIndex
== 0 &&
897 payload_size
== 17 &&
898 memcmp(payload
, "StartupLocality", sizeof("StartupLocality")) == 0) {
899 if (el
->startup_locality_found
)
900 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "StartupLocality event found twice!");
902 el
->startup_locality
= ((const uint8_t*) payload
)[sizeof("StartupLocality")];
903 el
->startup_locality_found
= true;
904 log_debug("Found StartupLocality event: %u", el
->startup_locality
);
908 if (event
->eventType
== EV_NO_ACTION
) { /* Ignore pseudo events, that don't result in a measurement */
909 log_debug("Skipping NO_ACTION event.");
913 r
= event_log_add_record(el
, &record
);
915 return log_error_errno(r
, "Failed to add record to event log: %m");
917 record
->pcr
= event
->pcrIndex
;
918 record
->source
= path
;
919 record
->firmware_event_type
= event
->eventType
;
920 record
->firmware_payload
= memdup(payload
, payload_size
);
921 if (!record
->firmware_payload
)
923 record
->firmware_payload_size
= payload_size
;
925 const void *ha
, *ha_next
= NULL
;
926 ha
= (const uint8_t*) event
+ offsetof(TCG_PCR_EVENT2
, digests
.digests
);
927 assert(event
->digests
.count
== n_algorithms
);
929 for (size_t i
= 0; i
< n_algorithms
; i
++, ha
= ha_next
) {
930 ha_next
= (const uint8_t*) ha
+ offsetof(TPMT_HA
, digest
) + algorithms
[i
].digestSize
;
932 /* The TPMT_HA is not aligned in the record, hence read the hashAlg field via an unaligned read */
933 assert_cc(__builtin_types_compatible_p(uint16_t, typeof(TPMI_ALG_HASH
)));
934 uint16_t hash_alg
= unaligned_read_ne16((const uint8_t*) ha
+ offsetof(TPMT_HA
, hashAlg
));
936 if (hash_alg
!= algorithms
[i
].algorithmId
)
937 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Hash algorithms in event log record don't match log.");
939 if (!tpm2_hash_alg_to_string(algorithms
[i
].algorithmId
))
942 r
= event_log_record_add_bank(
944 algorithms
[i
].algorithmId
,
945 (const uint8_t*) ha
+ offsetof(TPMT_HA
, digest
),
946 algorithms
[i
].digestSize
,
949 return log_error_errno(r
, "Failed to add bank to event log record: %m");
952 /* Try to extract a descriptive text */
953 r
= event_log_record_extract_firmware_description(record
);
957 r
= event_log_add_algorithms_from_record(el
, record
);
965 static int event_log_record_parse_json(EventLogRecord
*record
, JsonVariant
*j
) {
966 const char *rectype
= NULL
;
974 if (!json_variant_is_object(j
))
975 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "record object is not an object.");
977 x
= json_variant_by_key(j
, "pcr");
979 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field missing from TPM measurement log file entry.");
980 if (!json_variant_is_unsigned(x
))
981 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field is not an integer.");
983 u
= json_variant_unsigned(x
);
984 if (u
>= TPM2_PCRS_MAX
)
985 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'pcr' field is out of range.");
986 record
->pcr
= json_variant_unsigned(x
);
988 x
= json_variant_by_key(j
, "digests");
990 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field missing from TPM measurement log file entry.");
991 if (!json_variant_is_array(x
))
992 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field is not an array.");
994 JSON_VARIANT_ARRAY_FOREACH(k
, x
) {
995 _cleanup_free_
void *hash
= NULL
;
1000 a
= json_variant_by_key(k
, "hashAlg");
1002 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field element lacks 'hashAlg' field.");
1003 if (!json_variant_is_string(a
))
1004 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'hashAlg' field is not a string.");
1006 na
= tpm2_hash_alg_from_string(json_variant_string(a
));
1008 log_debug_errno(na
, "Unsupported hash '%s' in userspace event log, ignoring: %m", json_variant_string(a
));
1012 h
= json_variant_by_key(k
, "digest");
1014 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'digests' field lacks 'digest' field");
1016 r
= json_variant_unhex(h
, &hash
, &hash_size
);
1018 return log_error_errno(r
, "Failed to decode digest: %m");
1020 r
= event_log_record_add_bank(
1027 return log_error_errno(r
, "Failed to add bank to event log record: %m");
1030 x
= json_variant_by_key(j
, "content_type");
1032 log_debug("'content_type' missing from TPM measurement log file entry, ignoring.");
1034 if (!json_variant_is_string(x
))
1035 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content_type' field is not a string.");
1037 rectype
= json_variant_string(x
);
1040 if (streq_ptr(rectype
, "systemd")) {
1043 x
= json_variant_by_key(j
, "content");
1045 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content' field missing from TPM measurement log file entry.");
1046 if (!json_variant_is_object(x
))
1047 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'content' sub-object is not an object.");
1049 y
= json_variant_by_key(x
, "string");
1051 if (!json_variant_is_string(y
))
1052 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'string' field is not a string.");
1054 r
= free_and_strdup_warn(&record
->description
, json_variant_string(y
));
1059 y
= json_variant_by_key(x
, "eventType");
1061 if (!json_variant_is_string(y
))
1062 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'eventType' field is not a string.");
1064 record
->userspace_event_type
= tpm2_userspace_event_type_from_string(json_variant_string(y
));
1065 if (record
->userspace_event_type
< 0)
1066 log_debug_errno(record
->userspace_event_type
, "Unknown userspace event type '%s', ignoring.", json_variant_string(y
));
1069 json_variant_unref(record
->userspace_content
);
1070 record
->userspace_content
= json_variant_ref(x
);
1076 static int event_log_load_userspace(EventLog
*el
) {
1077 _cleanup_fclose_
FILE *f
= NULL
;
1078 _cleanup_free_
char *b
= NULL
;
1079 bool beginning
= true;
1086 path
= tpm2_userspace_log_path();
1088 f
= fopen(path
, "re");
1090 if (errno
!= ENOENT
)
1091 return log_error_errno(errno
, "Failed to open userspace TPM measurement log file: %m");
1096 if (flock(fileno(f
), LOCK_SH
) < 0)
1097 return log_error_errno(errno
, "Failed to lock userspace TPM measurement log file: %m");
1100 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
1101 EventLogRecord
*record
;
1107 return log_error_errno(errno
, "Failed to read local TPM measurement log file: %m");
1111 } else if (ch
!= 0x1EU
) {
1112 if (!GREEDY_REALLOC(b
, bn
+ 2))
1115 b
[bn
++] = (char) ch
;
1124 if (!GREEDY_REALLOC(b
, bn
+ 1))
1127 b
[bn
] = 0; /* Turn it into a string */
1129 if (memchr(b
, 0, bn
)) {
1130 log_warning("Found record with embedded NUL byte, skipping.");
1134 r
= json_parse(b
, 0, &j
, NULL
, NULL
);
1136 return log_error_errno(r
, "Failed to parse local TPM measurement log file: %m");
1138 r
= event_log_add_record(el
, &record
);
1140 return log_error_errno(r
, "Failed to add record to event log: %m");
1142 record
->source
= path
;
1144 r
= event_log_record_parse_json(record
, j
);
1148 r
= event_log_add_algorithms_from_record(el
, record
);
1162 static EventLog
*event_log_new(void) {
1163 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
1165 el
= new(EventLog
, 1);
1170 .primary_algorithm
= UINT16_MAX
,
1173 return TAKE_PTR(el
);
1176 static int event_log_load(EventLog
*el
) {
1181 r
= event_log_load_firmware(el
);
1185 r
= event_log_load_userspace(el
);
1192 static int event_log_read_pcrs(EventLog
*el
) {
1193 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
1198 r
= tpm2_context_new(NULL
, &tc
);
1202 FOREACH_ARRAY(rr
, el
->registers
, TPM2_PCRS_MAX
) {
1206 rr
->banks
= new0(EventLogRegisterBank
, el
->n_algorithms
);
1211 for (size_t a
= 0; a
< el
->n_algorithms
; a
++) {
1212 _cleanup_free_ Tpm2PCRValue
*pcr_values
= NULL
;
1213 size_t n_pcr_values
;
1214 TPML_PCR_SELECTION selection
;
1216 tpm2_tpml_pcr_selection_from_mask(TPM2_PCRS_MASK
, el
->algorithms
[a
], &selection
);
1217 r
= tpm2_pcr_read(tc
, &selection
, &pcr_values
, &n_pcr_values
);
1221 FOREACH_ARRAY(v
, pcr_values
, n_pcr_values
) {
1222 assert(v
->hash
== el
->algorithms
[a
]);
1223 el
->registers
[v
->index
].banks
[a
].observed
= v
->value
;
1230 static void event_log_initial_pcr_state(EventLog
*el
, uint32_t pcr
, size_t size
, TPM2B_DIGEST
*ret
) {
1232 assert(pcr
< TPM2_PCRS_MAX
);
1234 assert(size
<= sizeof_field(TPM2B_DIGEST
, buffer
));
1242 memzero(ret
->buffer
, ret
->size
-1);
1243 ((uint8_t*) ret
->buffer
)[ret
->size
-1] = el
->startup_locality_found
? el
->startup_locality
: 0;
1248 memzero(ret
->buffer
, ret
->size
);
1252 memset(ret
->buffer
, 0xffu
, ret
->size
);
1256 assert_not_reached();
1260 static int event_log_calculate_pcrs(EventLog
*el
) {
1263 /* Iterates through the event log an calculates the expected hash values based on all listed records */
1266 el
->mds
= new(const EVP_MD
*, el
->n_algorithms
);
1270 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1274 assert_se(a
= tpm2_hash_alg_to_string(el
->algorithms
[i
]));
1275 assert_se(md
= EVP_get_digestbyname(a
));
1280 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++)
1281 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1282 EventLogRegisterBank
*b
= el
->registers
[pcr
].banks
+ i
;
1283 event_log_initial_pcr_state(el
, pcr
, EVP_MD_size(el
->mds
[i
]), &b
->calculated
);
1286 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1287 EventLogRegister
*reg
= el
->registers
+ (*rr
)->pcr
;
1289 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
1290 const char *n
= tpm2_hash_alg_to_string(el
->algorithms
[i
]);
1291 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*mc
= NULL
;
1292 EventLogRegisterBank
*reg_b
;
1293 EventLogRecordBank
*rec_b
;
1296 rec_b
= event_log_record_find_bank(*rr
, el
->algorithms
[i
]);
1298 log_warning_errno(SYNTHETIC_ERRNO(ENXIO
), "Record with missing bank '%s', ignoring.", n
);
1302 reg_b
= reg
->banks
+ i
;
1304 mc
= EVP_MD_CTX_new();
1308 if (EVP_DigestInit_ex(mc
, el
->mds
[i
], NULL
) != 1)
1309 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to initialize %s message digest context.", n
);
1311 if (EVP_DigestUpdate(mc
, reg_b
->calculated
.buffer
, reg_b
->calculated
.size
) != 1)
1312 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
1314 if (EVP_DigestUpdate(mc
, rec_b
->hash
.buffer
, rec_b
->hash
.size
) != 1)
1315 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to run digest.");
1317 if (EVP_DigestFinal_ex(mc
, reg_b
->calculated
.buffer
, &sz
) != 1)
1318 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to finalize hash context.");
1320 assert(sz
== reg_b
->calculated
.size
);
1323 reg
->n_measurements
++;
1329 static int event_log_record_validate_hash_firmware(
1330 EventLogRecord
*record
,
1331 EventLogRecordBank
*bank
,
1334 _cleanup_free_
void *hdata_alternative
= NULL
;
1335 size_t hsz
, hsz_alternative
= 0;
1336 bool strict
= false;
1343 if (!EVENT_LOG_RECORD_IS_FIRMWARE(record
))
1346 switch (record
->firmware_event_type
) {
1349 case EV_EFI_GPT_EVENT
:
1350 case EV_EFI_VARIABLE_BOOT2
:
1351 case EV_EFI_VARIABLE_DRIVER_CONFIG
:
1352 case EV_EFI_VARIABLE_AUTHORITY
:
1354 case EV_S_CRTM_VERSION
:
1355 /* Here the extended hash value is the hash value of the event payload. Note that
1356 * EV_PLATFORM_CONFIG_FLAGS (according to the TCG PC Client Platform Firmware Profile
1357 * Specification) is also supposed to be like this. But ovmf doesn't follow this requirement,
1358 * hence be lenient on that one, and don't include it here. */
1359 hdata
= record
->firmware_payload
;
1360 hsz
= record
->firmware_payload_size
;
1364 case EV_EFI_VARIABLE_BOOT
: {
1365 const UEFI_VARIABLE_DATA
*vdata
= record
->firmware_payload
;
1368 /* Here the extended hash value is the hash value of the variable data (i.e. excluding the
1371 * Note: we already checked the general validity of the UEFI_VARIABLE_DATA structure, hence
1372 * no need to do so again. */
1374 assert(record
->firmware_payload_size
>= offsetof(UEFI_VARIABLE_DATA
, unicodeName
));
1375 skip
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + vdata
->unicodeNameLength
* 2;
1377 assert(record
->firmware_payload_size
>= skip
);
1378 hdata
= (const uint8_t*) record
->firmware_payload
+ skip
;
1379 hsz
= record
->firmware_payload_size
- skip
;
1385 if (event_log_record_is_stub(record
)) {
1386 /* The PE section names have a descriptive string in UTF-16 in the payload, but the
1387 * hash is over the UTF-8 version (with suffixing 0), hence let's convert the payload
1388 * into that format here, and see if it checks out. */
1389 hdata_alternative
= utf16_to_utf8(record
->firmware_payload
, record
->firmware_payload_size
);
1390 if (!hdata_alternative
)
1393 hsz_alternative
= strlen(hdata_alternative
) + 1; /* with NUL byte */
1399 /* For the others check the data too, just in case. But usually this will not match, hence
1400 * only report if the checksum matches, but don't complain if it does not. */
1401 hdata
= record
->firmware_payload
;
1402 hsz
= record
->firmware_payload_size
;
1407 int mdsz
= EVP_MD_size(md
);
1409 assert((size_t) mdsz
<= sizeof_field(TPM2B_DIGEST
, buffer
));
1411 TPM2B_DIGEST payload_hash
= {
1415 unsigned dsz
= mdsz
;
1417 if (EVP_Digest(hdata
, hsz
, payload_hash
.buffer
, &dsz
, md
, NULL
) != 1)
1418 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1419 assert(dsz
== (unsigned) mdsz
);
1421 /* If this didn't match then let's try the alternative format here, if we have one, and check things then. */
1422 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
.buffer
, payload_hash
.size
) != 0 && hdata_alternative
) {
1423 if (EVP_Digest(hdata_alternative
, hsz_alternative
, payload_hash
.buffer
, &dsz
, md
, NULL
) != 1)
1424 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1425 assert(dsz
== (unsigned) mdsz
);
1428 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
.buffer
, payload_hash
.size
) != 0) {
1430 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
1431 else if (record
->event_payload_valid
!= EVENT_PAYLOAD_VALID_NO
)
1432 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_DONT_KNOW
;
1433 } else if (record
->event_payload_valid
< 0)
1434 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_YES
;
1439 static int event_log_record_validate_hash_userspace(
1440 EventLogRecord
*record
,
1441 EventLogRecordBank
*bank
,
1444 _cleanup_free_
unsigned char *payload_hash
= NULL
;
1445 unsigned payload_hash_size
;
1454 if (!EVENT_LOG_RECORD_IS_USERSPACE(record
))
1457 if (!record
->userspace_content
)
1460 js
= json_variant_by_key(record
->userspace_content
, "string");
1464 assert(json_variant_is_string(js
));
1465 s
= json_variant_string(js
);
1467 mdsz
= EVP_MD_size(md
);
1470 payload_hash_size
= mdsz
;
1471 payload_hash
= malloc(payload_hash_size
);
1475 if (EVP_Digest(s
, strlen(s
), payload_hash
, &payload_hash_size
, md
, NULL
) != 1)
1476 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to calculate event payload hash.");
1478 assert((int) payload_hash_size
== mdsz
);
1479 if (memcmp_nn(bank
->hash
.buffer
, bank
->hash
.size
, payload_hash
, payload_hash_size
) != 0)
1480 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_NO
;
1481 else if (record
->event_payload_valid
< 0)
1482 record
->event_payload_valid
= EVENT_PAYLOAD_VALID_YES
;
1487 static int event_log_validate_record_hashes(EventLog
*el
) {
1492 /* For records which contain the full data to validate the hashes, do so. */
1494 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1496 LIST_FOREACH(banks
, bank
, (*rr
)->banks
) {
1500 assert_se(a
= tpm2_hash_alg_to_string(bank
->algorithm
));
1501 assert_se(md
= EVP_get_digestbyname(a
));
1503 r
= event_log_record_validate_hash_firmware(*rr
, bank
, md
);
1507 r
= event_log_record_validate_hash_userspace(*rr
, bank
, md
);
1516 static int event_log_component_cmp(EventLogComponent
*const*a
, EventLogComponent
*const*b
) {
1517 const EventLogComponent
*x
= ASSERT_PTR(*ASSERT_PTR(a
)), *y
= ASSERT_PTR(*ASSERT_PTR(b
));
1519 return strcmp(x
->id
, y
->id
);
1522 static EventLogComponent
*event_log_find_component(EventLog
*el
, const char *id
) {
1523 EventLogComponent k
= {
1526 EventLogComponent
*kk
= &k
, **found
;
1531 found
= typesafe_bsearch(
1535 event_log_component_cmp
);
1542 static int event_log_add_component(EventLog
*el
, const char *id
, EventLogComponent
**ret
) {
1543 _cleanup_(event_log_component_freep
) EventLogComponent
*component
= NULL
;
1544 _cleanup_free_
char *id_copy
= NULL
;
1545 EventLogComponent
*found
;
1550 found
= event_log_find_component(el
, id
);
1556 if (!GREEDY_REALLOC(el
->components
, el
->n_components
+1))
1559 id_copy
= strdup(id
);
1563 component
= new(EventLogComponent
, 1);
1567 *component
= (EventLogComponent
) {
1568 .id
= TAKE_PTR(id_copy
),
1574 el
->components
[el
->n_components
++] = TAKE_PTR(component
);
1578 static int event_log_record_equal(const EventLogRecord
*a
, const EventLogRecord
*b
) {
1579 EventLogRecordBank
*x
, *y
;
1582 assert(a
->event_log
);
1584 assert(b
->event_log
);
1585 assert(a
->event_log
== b
->event_log
);
1587 if (a
->pcr
!= b
->pcr
)
1590 x
= event_log_record_find_bank(a
, a
->event_log
->primary_algorithm
);
1591 y
= event_log_record_find_bank(b
, b
->event_log
->primary_algorithm
);
1595 assert(x
->algorithm
== a
->event_log
->primary_algorithm
);
1596 assert(y
->algorithm
== b
->event_log
->primary_algorithm
);
1598 return memcmp_nn(x
->hash
.buffer
, x
->hash
.size
, y
->hash
.buffer
, y
->hash
.size
) == 0;
1601 static int event_log_add_component_file(EventLog
*el
, EventLogComponent
*component
, const char *path
) {
1602 _cleanup_(event_log_component_variant_freep
) EventLogComponentVariant
*variant
= NULL
;
1603 _cleanup_free_
char *fname
= NULL
, *id
= NULL
, *path_copy
= NULL
;
1604 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
1605 JsonVariant
*records
;
1611 r
= path_extract_filename(path
, &fname
);
1613 return log_error_errno(r
, "Failed to extract basename from path %s: %m", path
);
1615 e
= endswith(fname
, ".pcrlock");
1617 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Bad suffix: %s", fname
);
1619 id
= strndup(fname
, e
- fname
);
1624 r
= event_log_add_component(el
, id
, &component
);
1629 if (!GREEDY_REALLOC(component
->variants
, component
->n_variants
+1))
1632 r
= json_parse_file(
1637 /* ret_line= */ NULL
,
1638 /* ret_column= */ NULL
);
1640 log_warning_errno(r
, "Failed to parse component file %s, ignoring: %m", path
);
1644 if (!json_variant_is_object(j
)) {
1645 log_warning_errno(r
, "Component file %s does not contain JSON object, ignoring.", path
);
1649 path_copy
= strdup(path
);
1653 variant
= new(EventLogComponentVariant
, 1);
1657 *variant
= (EventLogComponentVariant
) {
1658 .component
= component
,
1659 .path
= TAKE_PTR(path_copy
),
1663 records
= json_variant_by_key(j
, "records");
1667 if (!json_variant_is_array(records
)) {
1668 log_warning_errno(r
, "Component records field of file %s is not an array, ignoring.", path
);
1672 JSON_VARIANT_ARRAY_FOREACH(rj
, records
) {
1673 _cleanup_(event_log_record_freep
) EventLogRecord
*record
= NULL
;
1675 if (!GREEDY_REALLOC(variant
->records
, variant
->n_records
+1))
1678 record
= event_log_record_new(el
);
1682 r
= event_log_record_parse_json(record
, rj
);
1686 record
->owning_component_variant
= variant
;
1687 variant
->records
[variant
->n_records
++] = TAKE_PTR(record
);
1691 component
->variants
[component
->n_variants
++] = TAKE_PTR(variant
);
1695 static int event_log_add_component_dir(EventLog
*el
, const char *path
, char **base_search
) {
1696 _cleanup_free_
char *fname
= NULL
, *id
= NULL
;
1697 _cleanup_strv_free_
char **files
= NULL
;
1698 EventLogComponent
*component
;
1704 r
= path_extract_filename(path
, &fname
);
1706 return log_error_errno(r
, "Failed to extract basename from path %s: %m", path
);
1708 e
= endswith(fname
, ".pcrlock.d");
1710 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Bad suffix: %s", fname
);
1712 id
= strndup(fname
, e
- fname
);
1716 r
= event_log_add_component(el
, id
, &component
);
1720 _cleanup_strv_free_
char **search
= NULL
;
1722 STRV_FOREACH(b
, base_search
) {
1723 _cleanup_free_
char *q
= NULL
;
1725 q
= path_join(*b
, fname
);
1729 r
= strv_consume(&search
, TAKE_PTR(q
));
1734 r
= conf_files_list_strv(&files
, ".pcrlock", /* root= */ NULL
, CONF_FILES_REGULAR
, (const char*const*) search
);
1736 return log_error_errno(r
, "Failed to enumerate .pcrlock files for component '%s': %m", id
);
1738 STRV_FOREACH(f
, files
) {
1739 r
= event_log_add_component_file(el
, component
, *f
);
1747 static int event_log_load_components(EventLog
*el
) {
1748 _cleanup_strv_free_
char **files
= NULL
;
1754 dirs
= arg_components
?:
1755 STRV_MAKE("/etc/pcrlock.d",
1757 "/var/lib/pcrlock.d",
1758 "/usr/local/lib/pcrlock.d",
1759 "/usr/lib/pcrlock.d");
1761 r
= conf_files_list_strv(&files
, NULL
, NULL
, CONF_FILES_REGULAR
|CONF_FILES_DIRECTORY
|CONF_FILES_FILTER_MASKED
, (const char*const*) dirs
);
1763 return log_error_errno(r
, "Failed to enumerate .pcrlock files: %m");
1765 STRV_FOREACH(f
, files
) {
1766 if (endswith(*f
, ".pcrlock.d"))
1767 r
= event_log_add_component_dir(el
, *f
, dirs
);
1768 else if (endswith(*f
, ".pcrlock"))
1769 r
= event_log_add_component_file(el
, NULL
, *f
);
1779 static int event_log_validate_fully_recognized(EventLog
*el
) {
1781 for (uint32_t pcr
= 0; pcr
< ELEMENTSOF(el
->registers
); pcr
++) {
1782 bool fully_recognized
= true;
1784 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
1785 EventLogRecord
*rec
= *rr
;
1787 if (rec
->pcr
!= pcr
)
1790 if (rec
->n_mapped
== 0) {
1791 log_notice("Event log record %zu (PCR %" PRIu32
", \"%s\") not matching any component.",
1792 (size_t) (rr
- el
->records
), rec
->pcr
, strna(rec
->description
));
1793 fully_recognized
= false;
1798 el
->registers
[pcr
].fully_recognized
= fully_recognized
;
1804 static int event_log_match_component_variant(
1807 EventLogComponentVariant
*variant
,
1816 /* It's OK to point immediately after the last record, but not further */
1817 assert(i
<= el
->n_records
);
1818 assert(j
<= variant
->n_records
);
1820 /* All entries in the variant checked out? Yippieh! */
1821 if (j
== variant
->n_records
)
1824 /* If the remainder of the variant is longer than the remainder of the event log, it cannot possibly fit. */
1825 if (el
->n_records
- i
< variant
->n_records
- j
)
1828 /* Does this record match? If not, let's try at the next place in the logs. */
1829 if (!event_log_record_equal(el
->records
[i
], variant
->records
[j
]))
1830 return event_log_match_component_variant(el
, i
+ 1, variant
, j
, assign
); /* Recursion! */
1832 /* This one matches. Good. Let's see if the rest also matches. (Recursion!) */
1833 r
= event_log_match_component_variant(el
, i
+ 1, variant
, j
+ 1, assign
);
1838 /* Take ownership (Note we allow multiple components and variants to take owneship of the same record!) */
1839 if (!GREEDY_REALLOC(el
->records
[i
]->mapped
, el
->records
[i
]->n_mapped
+1))
1842 el
->records
[i
]->mapped
[el
->records
[i
]->n_mapped
++] = variant
;
1848 static uint32_t event_log_component_variant_pcrs(EventLogComponentVariant
*i
) {
1853 /* returns mask of PCRs touched by this variant */
1855 FOREACH_ARRAY(rr
, i
->records
, i
->n_records
)
1856 mask
|= UINT32_C(1) << (*rr
)->pcr
;
1861 static uint32_t event_log_component_pcrs(EventLogComponent
*c
) {
1866 /* Returns mask of PCRs touched by this component */
1868 FOREACH_ARRAY(ii
, c
->variants
, c
->n_variants
)
1869 mask
|= event_log_component_variant_pcrs(*ii
);
1874 static int event_log_map_components(EventLog
*el
) {
1875 _cleanup_free_
char *skipped_ids
= NULL
;
1876 unsigned n_skipped
= 0;
1881 FOREACH_ARRAY(cc
, el
->components
, el
->n_components
) {
1882 _cleanup_free_
char *matching_ids
= NULL
;
1883 unsigned n_matching
= 0, n_empty
= 0;
1884 EventLogComponent
*c
= *cc
;
1886 if (arg_location_end
&& strcmp(c
->id
, arg_location_end
) > 0) {
1889 if (!strextend_with_separator(&skipped_ids
, ", ", c
->id
))
1895 FOREACH_ARRAY(ii
, c
->variants
, c
->n_variants
) {
1896 EventLogComponentVariant
*i
= *ii
;
1898 if (i
->n_records
== 0) {
1899 /* The empty variant always matches */
1904 r
= event_log_match_component_variant(el
, 0, i
, 0, n_matching
+ n_empty
== 0);
1910 if (!strextend_with_separator(&matching_ids
, ", ", i
->id
))
1915 if (n_matching
+ n_empty
== 0) {
1917 if (arg_location_start
&& strcmp(c
->id
, arg_location_start
) >= 0)
1918 log_info("Didn't find component '%s' in event log, assuming system hasn't reached it yet.", c
->id
);
1920 log_notice("Couldn't find component '%s' in event log.", c
->id
);
1921 el
->n_missing_components
++;
1922 el
->missing_component_pcrs
|= event_log_component_pcrs(c
);
1924 } else if (n_matching
> 1)
1925 log_debug("Found %u possible variants of component '%s' in event log (%s). Proceeding.", n_matching
, c
->id
, matching_ids
);
1929 log_notice("Skipped %u components after location '%s' (%s).", n_skipped
, arg_location_end
, skipped_ids
);
1930 if (el
->n_missing_components
> 0)
1931 log_notice("Unable to recognize %zu components in event log.", el
->n_missing_components
);
1933 return event_log_validate_fully_recognized(el
);
1936 #define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
1938 static const char *ansi_true_color(uint8_t r
, uint8_t g
, uint8_t b
, char ret
[static ANSI_TRUE_COLOR_MAX
]) {
1939 snprintf(ret
, ANSI_TRUE_COLOR_MAX
, "\x1B[38;2;%u;%u;%um", r
, g
, b
);
1943 static char *color_for_pcr(EventLog
*el
, uint32_t pcr
) {
1944 char color
[ANSI_TRUE_COLOR_MAX
];
1948 assert(pcr
< TPM2_PCRS_MAX
);
1950 if (el
->registers
[pcr
].color
)
1951 return el
->registers
[pcr
].color
;
1953 hsv_to_rgb(360.0 / (TPM2_PCRS_MAX
- 1) * pcr
, 100, 90, &r
, &g
, &b
);
1954 ansi_true_color(r
, g
, b
, color
);
1956 el
->registers
[pcr
].color
= strdup(color
);
1957 return el
->registers
[pcr
].color
;
1960 static int add_algorithm_columns(
1964 const char *json_field_prefix
) {
1971 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
1972 const char *n
= tpm2_hash_alg_to_string(*alg
);
1973 _cleanup_free_
char *v
= NULL
;
1976 v
= strjoin(prefix
, " ", n
);
1981 size_t c
= table_get_current_column(table
);
1983 r
= table_add_cell(table
, NULL
, TABLE_HEADER
, v
?: n
);
1985 return table_log_add_error(r
);
1987 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
) &&
1988 el
->primary_algorithm
!= UINT16_MAX
&&
1989 *alg
!= el
->primary_algorithm
)
1990 (void) table_hide_column_from_display(table
, c
);
1992 _cleanup_free_
char *j
= NULL
;
1993 if (json_field_prefix
) {
1994 _cleanup_free_
char *m
= strdup(n
);
1998 j
= strjoin(json_field_prefix
, ascii_strupper(m
));
2003 (void) table_set_json_field_name(table
, c
, j
?: n
);
2009 static int show_log_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2010 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2015 table
= table_new_raw(5 + el
->n_algorithms
+ 4);
2019 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2021 r
= table_add_many(table
,
2022 TABLE_HEADER
, "pcr",
2023 TABLE_SET_ALIGN_PERCENT
, 100,
2025 TABLE_HEADER
, "pcrname",
2026 TABLE_HEADER
, "event",
2027 TABLE_HEADER
, "match",
2028 TABLE_SET_ALIGN_PERCENT
, 100);
2030 return table_log_add_error(r
);
2032 r
= add_algorithm_columns(el
, table
, NULL
, NULL
);
2036 size_t phase_column
= table_get_current_column(table
);
2038 r
= table_add_many(table
,
2039 TABLE_HEADER
, "F/U",
2040 TABLE_HEADER
, "source",
2041 TABLE_HEADER
, "component",
2042 TABLE_HEADER
, "description");
2044 return table_log_add_error(r
);
2046 (void) table_hide_column_from_display(table
, table_get_columns(table
) - 3); /* hide source */
2048 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2049 (void) table_hide_column_from_display(table
, (size_t) 1); /* hide color block column */
2051 (void) table_set_json_field_name(table
, phase_column
, "phase");
2053 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2054 EventLogRecord
*record
= *rr
;
2056 r
= table_add_many(table
,
2057 TABLE_UINT32
, record
->pcr
,
2058 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2059 TABLE_SET_COLOR
, color_for_pcr(el
, record
->pcr
),
2060 TABLE_STRING
, tpm2_pcr_index_to_string(record
->pcr
));
2062 return table_log_add_error(r
);
2064 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2067 et
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2069 r
= table_add_cell(table
, NULL
, TABLE_STRING
, et
);
2071 r
= table_add_cell(table
, NULL
, TABLE_UINT32_HEX
, &record
->firmware_event_type
);
2072 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
))
2073 r
= table_add_cell(table
, NULL
, TABLE_STRING
, tpm2_userspace_event_type_to_string(record
->userspace_event_type
));
2075 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2077 return table_log_add_error(r
);
2079 if (record
->event_payload_valid
< 0 || record
->event_payload_valid
== EVENT_PAYLOAD_VALID_DONT_KNOW
)
2080 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2082 r
= table_add_many(table
,
2083 TABLE_BOOLEAN_CHECKMARK
, record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
,
2084 TABLE_SET_COLOR
, ansi_highlight_green_red(record
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
));
2086 return table_log_add_error(r
);
2088 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2089 EventLogRecordBank
*bank
;
2091 bank
= event_log_record_find_bank(record
, *alg
);
2093 _cleanup_free_
char *hex
= NULL
;
2095 hex
= hexmem(bank
->hash
.buffer
, bank
->hash
.size
);
2099 r
= table_add_cell(table
, NULL
, TABLE_STRING
, hex
);
2101 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2103 return table_log_add_error(r
);
2106 r
= table_add_many(table
,
2107 TABLE_STRING
, EVENT_LOG_RECORD_IS_FIRMWARE(record
) ? "F" :
2108 EVENT_LOG_RECORD_IS_USERSPACE(record
) ? "U" : NULL
,
2109 TABLE_PATH_BASENAME
, record
->source
,
2110 TABLE_PATH_BASENAME
, record
->n_mapped
> 0 ? record
->mapped
[0]->component
->id
: NULL
,
2111 TABLE_STRING
, record
->description
);
2113 return table_log_add_error(r
);
2117 r
= table_to_json(table
, ret_variant
);
2119 return log_error_errno(r
, "Failed to format table to JSON: %m");
2124 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */true);
2126 return log_error_errno(r
, "Failed to output table: %m");
2131 static bool is_unset_pcr(const void *value
, size_t size
) {
2132 return memeqzero(value
, size
) || memeqbyte(0xffu
, value
, size
);
2135 static bool event_log_pcr_checks_out(const EventLog
*el
, const EventLogRegister
*reg
) {
2139 for (size_t i
= 0; i
< el
->n_algorithms
; i
++)
2140 if (memcmp_nn(reg
->banks
[i
].calculated
.buffer
, reg
->banks
[i
].calculated
.size
,
2141 reg
->banks
[i
].observed
.buffer
, reg
->banks
[i
].observed
.size
) != 0)
2147 static int show_pcr_table(EventLog
*el
, JsonVariant
**ret_variant
) {
2148 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2153 table
= table_new_raw(8 + el
->n_algorithms
*2);
2157 (void) table_set_ersatz_string(table
, TABLE_ERSATZ_DASH
);
2159 r
= table_add_many(table
,
2160 TABLE_HEADER
, "pcr",
2161 TABLE_SET_ALIGN_PERCENT
, 100,
2162 TABLE_HEADER
, "", /* color block column */
2163 TABLE_HEADER
, "", /* emoji column */
2164 TABLE_HEADER
, "pcrname",
2165 TABLE_HEADER
, "count",
2166 TABLE_SET_ALIGN_PERCENT
, 100,
2168 TABLE_SET_ALIGN_PERCENT
, 100,
2170 TABLE_SET_ALIGN_PERCENT
, 100,
2172 TABLE_SET_ALIGN_PERCENT
, 100);
2174 return table_log_add_error(r
);
2176 r
= add_algorithm_columns(el
, table
, "Calculated", "calculated");
2180 r
= add_algorithm_columns(el
, table
, "Observed", "observed");
2184 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2185 (void) table_hide_column_from_display(table
, (size_t) 1, (size_t) 2); /* hide color block and emoji column */
2186 else if (!emoji_enabled())
2187 (void) table_hide_column_from_display(table
, (size_t) 2);
2189 (void) table_set_json_field_name(table
, 5, "hashMatchesEventLog");
2190 (void) table_set_json_field_name(table
, 6, "allEventsMatched");
2191 (void) table_set_json_field_name(table
, 7, "noMissingComponents");
2193 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2194 /* Check if the PCR hash value matches the event log data */
2195 bool hash_match
= event_log_pcr_checks_out(el
, el
->registers
+ pcr
);
2197 /* Whether all records in this PCR have a matching component */
2198 bool fully_recognized
= el
->registers
[pcr
].fully_recognized
;
2200 /* Whether any unmatched components touch this PCR */
2201 bool missing_components
= FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
);
2203 const char *emoji
= special_glyph(
2204 !hash_match
? SPECIAL_GLYPH_DEPRESSED_SMILEY
:
2205 !fully_recognized
? SPECIAL_GLYPH_UNHAPPY_SMILEY
:
2206 missing_components
? SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY
:
2207 SPECIAL_GLYPH_HAPPY_SMILEY
);
2209 r
= table_add_many(table
,
2211 TABLE_STRING
, special_glyph(SPECIAL_GLYPH_FULL_BLOCK
),
2212 TABLE_SET_COLOR
, color_for_pcr(el
, pcr
),
2213 TABLE_STRING
, emoji
,
2214 TABLE_STRING
, tpm2_pcr_index_to_string(pcr
));
2216 return table_log_add_error(r
);
2218 if (el
->registers
[pcr
].n_measurements
> 0)
2219 r
= table_add_cell(table
, NULL
, TABLE_UINT
, &el
->registers
[pcr
].n_measurements
);
2221 r
= table_add_cell(table
, NULL
, TABLE_EMPTY
, NULL
);
2223 return table_log_add_error(r
);
2225 r
= table_add_many(table
,
2226 TABLE_BOOLEAN_CHECKMARK
, hash_match
,
2227 TABLE_SET_COLOR
, ansi_highlight_green_red(hash_match
),
2228 TABLE_BOOLEAN_CHECKMARK
, fully_recognized
,
2229 TABLE_SET_COLOR
, ansi_highlight_green_red(fully_recognized
),
2230 TABLE_BOOLEAN_CHECKMARK
, !missing_components
,
2231 TABLE_SET_COLOR
, ansi_highlight_green_red(!missing_components
));
2233 return table_log_add_error(r
);
2235 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2238 color
= is_unset_pcr(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
) ? ANSI_GREY
: NULL
;
2240 if (el
->registers
[pcr
].banks
[i
].calculated
.size
> 0) {
2241 _cleanup_free_
char *hex
= NULL
;
2243 hex
= hexmem(el
->registers
[pcr
].banks
[i
].calculated
.buffer
, el
->registers
[pcr
].banks
[i
].calculated
.size
);
2247 r
= table_add_many(table
,
2249 TABLE_SET_COLOR
, color
);
2251 r
= table_add_many(table
,
2253 TABLE_SET_COLOR
, color
);
2255 return table_log_add_error(r
);
2258 for (size_t i
= 0; i
< el
->n_algorithms
; i
++) {
2259 _cleanup_free_
char *hex
= NULL
;
2262 hex
= hexmem(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
);
2266 color
= !hash_match
? ANSI_HIGHLIGHT_RED
:
2267 is_unset_pcr(el
->registers
[pcr
].banks
[i
].observed
.buffer
, el
->registers
[pcr
].banks
[i
].observed
.size
) ? ANSI_GREY
: NULL
;
2269 r
= table_add_many(table
,
2271 TABLE_SET_COLOR
, color
);
2273 return table_log_add_error(r
);
2278 r
= table_to_json(table
, ret_variant
);
2280 return log_error_errno(r
, "Failed to format table to JSON: %m");
2285 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2287 return log_error_errno(r
, "Failed to output table: %m");
2289 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
))
2291 "%sLegend: H → PCR hash value matches event log%s\n"
2292 "%s R → All event log records for this PCR have a matching component%s\n"
2293 "%s C → No components that couldn't be matched with log records affect this PCR%s\n",
2294 ansi_grey(), ansi_normal(), /* less on small screens automatically resets the color after long lines, hence we set it anew for each line */
2295 ansi_grey(), ansi_normal(),
2296 ansi_grey(), ansi_normal());
2301 static int event_determine_primary_algorithm(EventLog
*el
) {
2304 if (el
->n_algorithms
== 0) {
2305 /* Nothing loaded to make the decision on? Then pick SHA256 */
2306 el
->primary_algorithm
= TPM2_ALG_SHA256
;
2310 FOREACH_ARRAY(alg
, el
->algorithms
, el
->n_algorithms
) {
2311 /* If we have SHA256, focus on that that */
2313 if (*alg
== TPM2_ALG_SHA256
) {
2314 el
->primary_algorithm
= *alg
;
2319 /* Otherwise show the "best" (i.e. the one with the highest id value) */
2320 el
->primary_algorithm
= el
->algorithms
[el
->n_algorithms
-1];
2324 static int event_log_load_and_process(EventLog
**ret
) {
2325 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2328 el
= event_log_new();
2332 r
= event_log_add_algorithms_from_environment(el
);
2336 r
= event_log_load(el
);
2340 r
= event_log_read_pcrs(el
);
2344 r
= event_log_calculate_pcrs(el
);
2348 r
= event_log_validate_record_hashes(el
);
2352 r
= event_determine_primary_algorithm(el
);
2356 r
= event_log_load_components(el
);
2360 r
= event_log_map_components(el
);
2364 *ret
= TAKE_PTR(el
);
2368 static int verb_show_log(int argc
, char *argv
[], void *userdata
) {
2369 _cleanup_(json_variant_unrefp
) JsonVariant
*log_table
= NULL
, *pcr_table
= NULL
;
2370 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2371 bool want_json
= !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
);
2374 r
= event_log_load_and_process(&el
);
2381 r
= show_log_table(el
, want_json
? &log_table
: NULL
);
2388 r
= show_pcr_table(el
, want_json
? &pcr_table
: NULL
);
2393 _cleanup_(json_variant_unrefp
) JsonVariant
*object
= NULL
;
2395 r
= json_build(&object
, JSON_BUILD_OBJECT(
2396 JSON_BUILD_PAIR_VARIANT("log", log_table
),
2397 JSON_BUILD_PAIR_VARIANT("pcrs", pcr_table
)));
2399 return log_error_errno(r
, "Failed to generate combined object: %m");
2401 r
= json_variant_dump(object
, arg_json_format_flags
, stdout
, /* prefix= */ NULL
);
2403 return log_error_errno(r
, "Failed to dump JSON object: %m");
2409 static int verb_show_cel(int argc
, char *argv
[], void *userdata
) {
2410 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2411 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2412 uint64_t recnum
= 0;
2415 el
= event_log_new();
2419 r
= event_log_load(el
);
2423 /* Output the event log in TCG CEL-JSON. */
2425 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2426 _cleanup_(json_variant_unrefp
) JsonVariant
*ja
= NULL
, *fj
= NULL
;
2427 EventLogRecord
*record
= *rr
;
2428 JsonVariant
*cd
= NULL
;
2429 const char *ct
= NULL
;
2431 LIST_FOREACH(banks
, bank
, record
->banks
) {
2432 r
= json_variant_append_arrayb(
2433 &ja
, JSON_BUILD_OBJECT(
2434 JSON_BUILD_PAIR_STRING("hashAlg", tpm2_hash_alg_to_string(bank
->algorithm
)),
2435 JSON_BUILD_PAIR_HEX("digest", bank
->hash
.buffer
, bank
->hash
.size
)));
2437 return log_error_errno(r
, "Failed to append CEL digest entry: %m");
2441 r
= json_variant_new_array(&ja
, NULL
, 0);
2443 return log_error_errno(r
, "Failed to allocate JSON array: %m");
2446 if (EVENT_LOG_RECORD_IS_FIRMWARE(record
)) {
2447 _cleanup_free_
char *et
= NULL
;
2450 z
= tpm2_log_event_type_to_string(record
->firmware_event_type
);
2452 _cleanup_free_
char *b
= NULL
;
2454 b
= strreplace(z
, "-", "_");
2458 et
= strjoin("EV_", ascii_strupper(b
));
2461 } else if (asprintf(&et
, "%" PRIu32
, record
->firmware_event_type
) < 0)
2464 r
= json_build(&fj
, JSON_BUILD_OBJECT(
2465 JSON_BUILD_PAIR_STRING("event_type", et
),
2466 JSON_BUILD_PAIR_HEX("event_data", record
->firmware_payload
, record
->firmware_payload_size
)));
2468 return log_error_errno(r
, "Failed to build firmware event data: %m");
2471 ct
= "pcclient_std";
2472 } else if (EVENT_LOG_RECORD_IS_USERSPACE(record
)) {
2473 cd
= record
->userspace_content
;
2477 r
= json_variant_append_arrayb(&array
,
2479 JSON_BUILD_PAIR_UNSIGNED("pcr", record
->pcr
),
2480 JSON_BUILD_PAIR_UNSIGNED("recnum", ++recnum
),
2481 JSON_BUILD_PAIR_VARIANT("digests", ja
),
2482 JSON_BUILD_PAIR_CONDITION(ct
, "content_type", JSON_BUILD_STRING(ct
)),
2483 JSON_BUILD_PAIR_CONDITION(cd
, "content", JSON_BUILD_VARIANT(cd
))));
2485 return log_error_errno(r
, "Failed to append CEL record: %m");
2488 if (arg_json_format_flags
& (JSON_FORMAT_PRETTY
|JSON_FORMAT_PRETTY_AUTO
))
2489 pager_open(arg_pager_flags
);
2491 json_variant_dump(array
, arg_json_format_flags
|JSON_FORMAT_EMPTY_ARRAY
, stdout
, NULL
);
2495 static int verb_list_components(int argc
, char *argv
[], void *userdata
) {
2496 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2497 _cleanup_(table_unrefp
) Table
*table
= NULL
;
2502 } loc
= BEFORE_LOCATION
;
2505 el
= event_log_new();
2509 r
= event_log_add_algorithms_from_environment(el
);
2513 r
= event_determine_primary_algorithm(el
);
2517 r
= event_log_load_components(el
);
2521 table
= table_new("id", "variants");
2525 FOREACH_ARRAY(c
, el
->components
, el
->n_components
) {
2527 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2528 _cleanup_free_
char *marker
= NULL
;
2532 case BEFORE_LOCATION
:
2533 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2534 loc
= AFTER_LOCATION
;
2535 marker
= strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), " location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_LEFT
));
2536 } else if (arg_location_start
&& strcmp((*c
)->id
, arg_location_start
) >= 0) {
2537 loc
= BETWEEN_LOCATION
;
2538 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_TOP
), " start location '", arg_location_start
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_DOWN
));
2543 case BETWEEN_LOCATION
:
2544 if (arg_location_end
&& strcmp((*c
)->id
, arg_location_end
) >= 0) {
2545 loc
= AFTER_LOCATION
;
2546 marker
= strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), " end location '", arg_location_end
, "' ", special_glyph(SPECIAL_GLYPH_ARROW_UP
));
2550 case AFTER_LOCATION
:
2555 r
= table_add_many(table
,
2556 TABLE_STRING
, marker
,
2557 TABLE_SET_COLOR
, ANSI_GREY
,
2560 return table_log_add_error(r
);
2564 FOREACH_ARRAY(variant
, (*c
)->variants
, (*c
)->n_variants
) {
2565 r
= table_add_many(table
,
2566 TABLE_STRING
, (*c
)->id
,
2567 TABLE_PATH
, (*variant
)->path
);
2569 return table_log_add_error(r
);
2573 if (table_get_rows(table
) > 1 || !FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2574 r
= table_print_with_pager(table
, arg_json_format_flags
, arg_pager_flags
, /* show_header= */ true);
2576 return log_error_errno(r
, "Failed to output table: %m");
2579 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
2580 if (table_get_rows(table
) > 1)
2581 printf("\n%zu components listed.\n", table_get_rows(table
) - 1);
2583 printf("No components defined.\n");
2589 static int event_log_pcr_mask_checks_out(EventLog
*el
, uint32_t mask
) {
2592 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
2594 if (!FLAGS_SET(mask
, UINT32_C(1) << pcr
))
2597 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
))
2598 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Event log for PCR %" PRIu32
" does not match PCR state, refusing.", pcr
);
2604 static int make_pcrlock_record(
2608 JsonVariant
**ret_record
) {
2610 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
2613 assert(data
|| data_size
== 0);
2616 if (data_size
== SIZE_MAX
)
2617 data_size
= strlen(data
);
2619 /* Generates a .pcrlock record for the given PCR and data/data size. This is a subset of TCG CEL. */
2621 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
2622 _cleanup_free_
unsigned char *hash
= NULL
;
2624 unsigned hash_usize
;
2628 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
2629 assert_se(md
= EVP_get_digestbyname(a
));
2630 hash_ssize
= EVP_MD_size(md
);
2631 assert_se(hash_ssize
> 0);
2632 hash_usize
= hash_ssize
;
2634 hash
= malloc(hash_usize
);
2638 if (EVP_Digest(data
, data_size
, hash
, &hash_usize
, md
, NULL
) != 1)
2639 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data with algorithm '%s'.", a
);
2641 r
= json_variant_append_arrayb(
2644 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
2645 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_usize
))));
2647 return log_error_errno(r
, "Failed to build JSON digest object: %m");
2650 r
= json_build(ret_record
,
2652 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(pcr
)),
2653 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
2655 return log_error_errno(r
, "Failed to build record object: %m");
2660 static const char *pcrlock_path(const char *default_pcrlock_path
) {
2661 return arg_pcrlock_path
?: arg_pcrlock_auto
? default_pcrlock_path
: NULL
;
2664 static int write_pcrlock(JsonVariant
*array
, const char *default_pcrlock_path
) {
2665 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *a
= NULL
;
2666 _cleanup_fclose_
FILE *f
= NULL
;
2671 r
= json_variant_new_array(&a
, NULL
, 0);
2673 return log_error_errno(r
, "Failed to allocate empty array: %m");
2678 r
= json_build(&v
, JSON_BUILD_OBJECT(
2679 JSON_BUILD_PAIR("records", JSON_BUILD_VARIANT(array
))));
2681 return log_error_errno(r
, "Failed to build JSON object: %m");
2683 p
= pcrlock_path(default_pcrlock_path
);
2685 (void) mkdir_parents_label(p
, 0755);
2689 return log_error_errno(errno
, "Failed to open %s for writing: %m", p
);
2692 r
= json_variant_dump(v
, arg_json_format_flags
, f
?: stdout
, /* prefix= */ NULL
);
2694 return log_error_errno(r
, "Failed to output JSON object: %m");
2697 log_info("%s written.", p
);
2702 static int unlink_pcrlock(const char *default_pcrlock_path
) {
2705 p
= pcrlock_path(default_pcrlock_path
);
2707 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No .pcrlock path specified, refusing.");
2709 if (unlink(p
) < 0) {
2710 if (errno
!= ENOENT
)
2711 return log_error_errno(errno
, "Failed to delete %s: %m", p
);
2713 log_info("%s already deleted.", p
);
2715 log_info("%s deleted.", p
);
2717 (void) rmdir_parents(p
, "/var/lib");
2722 static int verb_lock_raw(int argc
, char *argv
[], void *userdata
) {
2723 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2724 _cleanup_free_
char *data
= NULL
;
2725 _cleanup_fclose_
FILE *f
= NULL
;
2729 if (arg_pcr_mask
== 0)
2730 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No PCR specified, refusing.");
2733 f
= fopen(argv
[1], "re");
2735 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
2738 r
= read_full_stream(f
?: stdin
, &data
, &size
);
2740 return log_error_errno(r
, "Failed to read data from stdin: %m");
2742 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
2743 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2745 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
2748 r
= make_pcrlock_record(i
, data
, size
, &record
);
2752 r
= json_variant_append_array(&array
, record
);
2754 return log_error_errno(r
, "Failed to append to JSON array: %m");
2757 return write_pcrlock(array
, NULL
);
2760 static int verb_unlock_simple(int argc
, char *argv
[], void *userdata
) {
2761 return unlink_pcrlock(NULL
);
2764 static int verb_lock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2765 static const struct {
2768 int synthesize_empty
; /* 0 → fail, > 0 → synthesize empty db, < 0 → skip */
2770 { EFI_VENDOR_GLOBAL
, "SecureBoot", 0 },
2771 { EFI_VENDOR_GLOBAL
, "PK", 0 },
2772 { EFI_VENDOR_GLOBAL
, "KEK", 0 },
2773 { EFI_VENDOR_DATABASE
, "db", 1 },
2774 { EFI_VENDOR_DATABASE
, "dbx", 1 },
2775 { EFI_VENDOR_DATABASE
, "dbt", -1 },
2776 { EFI_VENDOR_DATABASE
, "dbr", -1 },
2779 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2782 /* Generates expected records from the current SecureBoot state, as readable in the EFI variables
2785 FOREACH_ARRAY(vv
, variables
, ELEMENTSOF(variables
)) {
2786 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
;
2788 _cleanup_free_
char *name
= NULL
;
2789 if (asprintf(&name
, "%s-" SD_ID128_UUID_FORMAT_STR
, vv
->name
, SD_ID128_FORMAT_VAL(vv
->id
)) < 0)
2792 _cleanup_free_
void *data
= NULL
;
2794 r
= efi_get_variable(name
, NULL
, &data
, &data_size
);
2796 if (r
!= -ENOENT
|| vv
->synthesize_empty
== 0)
2797 return log_error_errno(r
, "Failed to read EFI variable '%s': %m", name
);
2798 if (vv
->synthesize_empty
< 0)
2801 /* If the main database variables are not set we don't consider this an error, but
2802 * measure an empty database instead. */
2803 log_debug("EFI variable %s is not set, synthesizing empty variable for measurement.", name
);
2807 _cleanup_free_ char16_t
* name16
= utf8_to_utf16(vv
->name
, SIZE_MAX
);
2810 size_t name16_bytes
= char16_strlen(name16
) * 2;
2812 size_t vdata_size
= offsetof(UEFI_VARIABLE_DATA
, unicodeName
) + name16_bytes
+ data_size
;
2813 _cleanup_free_ UEFI_VARIABLE_DATA
*vdata
= malloc(vdata_size
);
2817 *vdata
= (UEFI_VARIABLE_DATA
) {
2818 .unicodeNameLength
= name16_bytes
/ 2,
2819 .variableDataLength
= data_size
,
2822 efi_id128_to_guid(vv
->id
, vdata
->variableName
);
2823 memcpy(mempcpy(vdata
->unicodeName
, name16
, name16_bytes
), data
, data_size
);
2825 r
= make_pcrlock_record(TPM2_PCR_SECURE_BOOT_POLICY
/* =7 */, vdata
, vdata_size
, &record
);
2829 r
= json_variant_append_array(&array
, record
);
2831 return log_error_errno(r
, "Failed to append to JSON array: %m");
2834 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_POLICY_PATH
);
2837 static int verb_unlock_secureboot_policy(int argc
, char *argv
[], void *userdata
) {
2838 return unlink_pcrlock(PCRLOCK_SECUREBOOT_POLICY_PATH
);
2841 static int event_log_record_is_secureboot_variable(EventLogRecord
*rec
, sd_id128_t uuid
, const char *name
) {
2842 _cleanup_free_
char *found_name
= NULL
;
2843 sd_id128_t found_uuid
;
2849 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2852 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2855 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2858 if (rec
->firmware_event_type
!= EV_EFI_VARIABLE_DRIVER_CONFIG
)
2861 r
= event_log_record_parse_variable_data(rec
, &found_uuid
, &found_name
);
2867 if (!sd_id128_equal(found_uuid
, uuid
))
2870 return streq(found_name
, name
);
2873 static bool event_log_record_is_secureboot_authority(EventLogRecord
*rec
) {
2876 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
2879 if (rec
->pcr
!= TPM2_PCR_SECURE_BOOT_POLICY
)
2882 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
)
2885 return rec
->firmware_event_type
== EV_EFI_VARIABLE_AUTHORITY
;
2888 static int event_log_ensure_secureboot_consistency(EventLog
*el
) {
2889 static const struct {
2894 { EFI_VENDOR_GLOBAL
, "SecureBoot", true },
2895 { EFI_VENDOR_GLOBAL
, "PK", true },
2896 { EFI_VENDOR_GLOBAL
, "KEK", true },
2897 { EFI_VENDOR_DATABASE
, "db", true },
2898 { EFI_VENDOR_DATABASE
, "dbx", true },
2899 { EFI_VENDOR_DATABASE
, "dbt", false },
2900 { EFI_VENDOR_DATABASE
, "dbr", false },
2901 // FIXME: ensure we also find the separator here
2904 EventLogRecord
*records
[ELEMENTSOF(table
)] = {};
2905 EventLogRecord
*first_authority
= NULL
;
2909 /* Ensures that the PCR 7 records are complete and in order. Before we lock down PCR 7 we want to
2910 * ensure its state is actually consistent. */
2912 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
2913 EventLogRecord
*rec
= *rr
;
2914 size_t found
= SIZE_MAX
;
2916 if (event_log_record_is_secureboot_authority(rec
)) {
2917 if (first_authority
)
2920 first_authority
= rec
;
2921 // FIXME: also check that each authority record's data is also listed in 'db'
2925 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2926 if (event_log_record_is_secureboot_variable(rec
, table
[i
].id
, table
[i
].name
)) {
2930 if (found
== SIZE_MAX
)
2933 /* Require the authority records always come *after* database measurements */
2934 if (first_authority
)
2935 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "SecureBoot authority before variable, refusing.");
2937 /* Check for duplicates */
2939 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Duplicate '%s' record, refusing.", rec
->description
);
2941 /* Check for order */
2942 for (size_t j
= found
+ 1; j
< ELEMENTSOF(table
); j
++)
2944 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "'%s' record before '%s' record, refusing.", records
[j
]->description
, rec
->description
);
2946 records
[found
] = rec
;
2949 /* Check for existence */
2950 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++)
2951 if (table
[i
].required
&& !records
[i
])
2952 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Required record '%s' not found, refusing.", table
[i
].name
);
2954 /* At this point we know that all required variables have been measured, in the right order. */
2958 static int verb_lock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
2959 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
2960 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
2963 /* Lock down the EV_EFI_VARIABLE_AUTHORITY records from the existing log. Note that there's not too
2964 * much value in locking this down too much, since it stores only the result of the primary database
2965 * checks, and that's what we should bind policy to. Moreover it's hard to predict, since extension
2966 * card firmware validation will result in additional records here. */
2968 if (!is_efi_secure_boot()) {
2969 log_info("SecureBoot disabled, not generating authority .pcrlock file.");
2970 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
2973 el
= event_log_new();
2977 r
= event_log_add_algorithms_from_environment(el
);
2981 r
= event_log_load(el
);
2985 r
= event_log_read_pcrs(el
);
2989 r
= event_log_calculate_pcrs(el
);
2993 /* Before we base anything on the event log records, let's check that the event log state checks
2996 r
= event_log_pcr_mask_checks_out(el
, UINT32_C(1) << TPM2_PCR_SECURE_BOOT_POLICY
);
3000 r
= event_log_validate_record_hashes(el
);
3004 r
= event_log_ensure_secureboot_consistency(el
);
3008 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3009 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3010 EventLogRecord
*rec
= *rr
;
3012 if (!event_log_record_is_secureboot_authority(rec
))
3015 log_debug("Locking down authority '%s'.", strna(rec
->description
));
3017 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3018 r
= json_variant_append_arrayb(
3021 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3022 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3024 return log_error_errno(r
, "Failed to build digests array: %m");
3027 r
= json_variant_append_arrayb(
3030 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3031 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3033 return log_error_errno(r
, "Failed to build record array: %m");
3036 return write_pcrlock(array
, PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3039 static int verb_unlock_secureboot_authority(int argc
, char *argv
[], void *userdata
) {
3040 return unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH
);
3043 static int verb_lock_gpt(int argc
, char *argv
[], void *userdata
) {
3044 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *record
= NULL
;
3045 _cleanup_(sd_device_unrefp
) sd_device
*d
= NULL
;
3046 uint8_t h
[2 * 4096]; /* space for at least two 4K sectors. GPT header should definitely be in here */
3047 uint64_t start
, n_members
, member_size
;
3048 _cleanup_close_
int fd
= -EBADF
;
3054 r
= block_device_new_from_path(
3055 argc
>= 2 ? argv
[1] : "/",
3056 BLOCK_DEVICE_LOOKUP_WHOLE_DISK
|BLOCK_DEVICE_LOOKUP_BACKING
|BLOCK_DEVICE_LOOKUP_ORIGINATING
,
3059 return log_error_errno(r
, "Failed to determine root block device: %m");
3061 fd
= sd_device_open(d
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
);
3063 return log_error_errno(fd
, "Failed to open root block device: %m");
3065 n
= pread(fd
, &h
, sizeof(h
), 0);
3067 return log_error_errno(errno
, "Failed to read GPT header of block device: %m");
3068 if ((size_t) n
!= sizeof(h
))
3069 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read trying to read GPT header: %m");
3071 /* Try a couple of sector sizes */
3072 for (size_t sz
= 512; sz
<= 4096; sz
<<= 1) {
3073 assert(sizeof(h
) >= sz
* 2);
3074 p
= (const GptHeader
*) (h
+ sz
); /* 2nd sector */
3076 if (!gpt_header_has_signature(p
))
3080 return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ
),
3081 "Disk has partition table for multiple sector sizes, refusing.");
3087 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3088 "Disk does not have GPT partition table, refusing.");
3090 p
= (const GptHeader
*) (h
+ found
);
3092 if (le32toh(p
->header_size
) > found
)
3093 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3094 "GPT header size over long (%" PRIu32
"), refusing.", le32toh(p
->header_size
));
3096 start
= le64toh(p
->partition_entry_lba
);
3097 if (start
> UINT64_MAX
/ found
)
3098 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3099 "Partition table start offset overflow, refusing.");
3101 member_size
= le32toh(p
->size_of_partition_entry
);
3102 if (member_size
< sizeof(GptPartitionEntry
))
3103 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3104 "Partition entry size too short, refusing.");
3106 n_members
= le32toh(p
->number_of_partition_entries
);
3107 uint64_t member_bufsz
= n_members
* member_size
;
3108 if (member_bufsz
> 1U*1024U*1024U)
3109 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
3110 "Partition table size too large, refusing.");
3112 member_bufsz
= ROUND_UP(member_bufsz
, found
);
3114 _cleanup_free_
void *members
= malloc(member_bufsz
);
3118 n
= pread(fd
, members
, member_bufsz
, start
* found
);
3120 return log_error_errno(errno
, "Failed to read GPT partition table entries: %m");
3121 if ((size_t) n
!= member_bufsz
)
3122 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short read while reading GPT partition table entries: %m");
3124 size_t vdata_size
= le32toh(p
->header_size
) + sizeof(le64_t
) + member_size
* n_members
;
3125 _cleanup_free_
void *vdata
= malloc0(vdata_size
);
3129 void *n_measured_entries
= mempcpy(vdata
, p
, sizeof(GptHeader
)); /* n_measured_entries is a 64bit value */
3131 void *qq
= (uint8_t*) n_measured_entries
+ sizeof(le64_t
);
3133 for (uint64_t i
= 0; i
< n_members
; i
++) {
3134 const GptPartitionEntry
*entry
= (const GptPartitionEntry
*) ((const uint8_t*) members
+ (member_size
* i
));
3136 if (memeqzero(entry
->partition_type_guid
, sizeof(entry
->partition_type_guid
)))
3139 qq
= mempcpy(qq
, entry
, member_size
);
3140 unaligned_write_le64(n_measured_entries
, unaligned_read_le64(n_measured_entries
) + 1);
3143 vdata_size
= (uint8_t*) qq
- (uint8_t*) vdata
;
3145 r
= make_pcrlock_record(TPM2_PCR_BOOT_LOADER_CONFIG
/* =5 */, vdata
, vdata_size
, &record
);
3149 r
= json_variant_new_array(&array
, &record
, 1);
3151 return log_error_errno(r
, "Failed to append to JSON array: %m");
3153 return write_pcrlock(array
, PCRLOCK_GPT_PATH
);
3156 static int verb_unlock_gpt(int argc
, char *argv
[], void *userdata
) {
3157 return unlink_pcrlock(PCRLOCK_GPT_PATH
);
3160 static bool event_log_record_is_separator(const EventLogRecord
*rec
) {
3163 /* Recognizes EV_SEPARATOR events */
3165 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3168 if (rec
->firmware_event_type
!= EV_SEPARATOR
)
3171 return rec
->event_payload_valid
== EVENT_PAYLOAD_VALID_YES
; /* Insist the record is consistent */
3174 static int event_log_record_is_action_calling_efi_app(const EventLogRecord
*rec
) {
3175 _cleanup_free_
char *d
= NULL
;
3180 /* Recognizes the special EV_EFI_ACTION that is issues when the firmware passes control to the boot loader. */
3182 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3185 if (rec
->pcr
!= TPM2_PCR_BOOT_LOADER_CODE
)
3188 if (rec
->firmware_event_type
!= EV_EFI_ACTION
)
3191 if (rec
->event_payload_valid
!= EVENT_PAYLOAD_VALID_YES
) /* Insist the record is consistent */
3194 r
= make_cstring(rec
->firmware_payload
, rec
->firmware_payload_size
, MAKE_CSTRING_ALLOW_TRAILING_NUL
, &d
);
3198 return streq(d
, "Calling EFI Application from Boot Option");
3201 static void enable_json_sse(void) {
3202 /* We shall write this to a single output stream? We have to output two files, hence try to be smart
3203 * and enable JSON SSE */
3205 if (!arg_pcrlock_path
&& arg_pcrlock_auto
)
3208 if (FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_SSE
))
3211 log_notice("Enabling JSON_SEQ mode, since writing two .pcrlock files to single output.");
3212 arg_json_format_flags
|= JSON_FORMAT_SSE
;
3215 static int verb_lock_firmware(int argc
, char *argv
[], void *userdata
) {
3216 _cleanup_(json_variant_unrefp
) JsonVariant
*array_early
= NULL
, *array_late
= NULL
;
3217 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
3218 uint32_t always_mask
, separator_mask
, separator_seen_mask
= 0, action_seen_mask
= 0;
3219 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3224 /* The PCRs we intend to cover. Note that we measure firmware, external *and* boot loader code/config
3225 * here – but the latter only until the "separator" events are seen, which tell us where transition
3226 * into OS boot loader happens. This reflects the fact that on some systems the firmware already
3227 * measures some firmware-supplied apps into PCR 4. (e.g. Thinkpad X1 Gen9) */
3228 if (endswith(argv
[0], "firmware-code")) {
3229 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CODE
) | /* → 0 */
3230 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE
); /* → 2 */
3232 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
; /* → 4 */
3234 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3235 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3237 assert(endswith(argv
[0], "firmware-config"));
3238 always_mask
= (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG
) | /* → 1 */
3239 (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG
); /* → 3 */
3241 separator_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CONFIG
; /* → 5 */
3243 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3244 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3247 el
= event_log_new();
3251 r
= event_log_add_algorithms_from_environment(el
);
3255 r
= event_log_load(el
);
3259 r
= event_log_read_pcrs(el
);
3263 r
= event_log_calculate_pcrs(el
);
3267 r
= event_log_validate_record_hashes(el
);
3271 /* Before we base anything on the event log records for any of the selected PCRs, let's check that
3272 * the event log state checks out for them. */
3274 r
= event_log_pcr_mask_checks_out(el
, always_mask
|separator_mask
);
3278 // FIXME: before doing this, validate ahead-of-time that EV_SEPARATOR records exist for all entries,
3281 FOREACH_ARRAY(rr
, el
->records
, el
->n_records
) {
3282 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3283 EventLogRecord
*rec
= *rr
;
3284 uint32_t bit
= UINT32_C(1) << rec
->pcr
;
3286 if (!EVENT_LOG_RECORD_IS_FIRMWARE(rec
))
3289 if (!FLAGS_SET(always_mask
, bit
) &&
3290 !(FLAGS_SET(separator_mask
, bit
) && !FLAGS_SET(separator_seen_mask
|action_seen_mask
, bit
)))
3293 /* If we hit the separator record, we stop processing the PCRs listed in `separator_mask` */
3294 if (event_log_record_is_separator(rec
)) {
3295 separator_seen_mask
|= bit
;
3299 /* If we hit the special "Calling EFI Application from Boot Option" action we treat this the
3300 * same as a separator here, as that's where firmware passes control to boot loader. Note
3301 * that some EFI implementations forget to generate one of them. */
3302 r
= event_log_record_is_action_calling_efi_app(rec
);
3304 return log_error_errno(r
, "Failed to check if event is 'Calling EFI Application from Boot Option' action: %m");
3306 action_seen_mask
|= bit
;
3310 LIST_FOREACH(banks
, bank
, rec
->banks
) {
3311 r
= json_variant_append_arrayb(
3314 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(tpm2_hash_alg_to_string(bank
->algorithm
))),
3315 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(bank
->hash
.buffer
, bank
->hash
.size
))));
3317 return log_error_errno(r
, "Failed to build digests array: %m");
3320 r
= json_variant_append_arrayb(
3321 FLAGS_SET(separator_seen_mask
, bit
) ? &array_late
: &array_early
,
3323 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(rec
->pcr
)),
3324 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3326 return log_error_errno(r
, "Failed to build record array: %m");
3329 r
= write_pcrlock(array_early
, default_pcrlock_early_path
);
3333 return write_pcrlock(array_late
, default_pcrlock_late_path
);
3336 static int verb_unlock_firmware(int argc
, char *argv
[], void *userdata
) {
3337 const char *default_pcrlock_early_path
, *default_pcrlock_late_path
;
3340 if (endswith(argv
[0], "firmware-code")) {
3341 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CODE_EARLY_PATH
;
3342 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CODE_LATE_PATH
;
3344 default_pcrlock_early_path
= PCRLOCK_FIRMWARE_CONFIG_EARLY_PATH
;
3345 default_pcrlock_late_path
= PCRLOCK_FIRMWARE_CONFIG_LATE_PATH
;
3348 r
= unlink_pcrlock(default_pcrlock_early_path
);
3352 if (arg_pcrlock_path
) /* if the path is specified don't delete the same thing twice */
3355 r
= unlink_pcrlock(default_pcrlock_late_path
);
3362 static int verb_lock_machine_id(int argc
, char *argv
[], void *userdata
) {
3363 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3364 _cleanup_free_
char *word
= NULL
;
3367 r
= pcrextend_machine_id_word(&word
);
3371 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3375 r
= json_variant_new_array(&array
, &record
, 1);
3377 return log_error_errno(r
, "Failed to create record array: %m");
3379 return write_pcrlock(array
, PCRLOCK_MACHINE_ID_PATH
);
3382 static int verb_unlock_machine_id(int argc
, char *argv
[], void *userdata
) {
3383 return unlink_pcrlock(PCRLOCK_MACHINE_ID_PATH
);
3386 static int pcrlock_file_system_path(const char *normalized_path
, char **ret
) {
3387 _cleanup_free_
char *s
= NULL
;
3389 assert(normalized_path
);
3391 if (path_equal(normalized_path
, "/"))
3392 s
= strdup(PCRLOCK_ROOT_FILE_SYSTEM_PATH
);
3394 /* We reuse the escaping we use for turning paths into unit names */
3395 _cleanup_free_
char *escaped
= NULL
;
3397 assert(normalized_path
[0] == '/');
3398 assert(normalized_path
[1] != '/');
3400 escaped
= unit_name_escape(normalized_path
+ 1);
3404 s
= strjoin(PCRLOCK_FILE_SYSTEM_PATH_PREFIX
, escaped
, ".pcrlock");
3413 static int verb_lock_file_system(int argc
, char *argv
[], void *userdata
) {
3414 const char* paths
[3] = {};
3423 r
= get_block_device("/", &a
);
3425 return log_error_errno(r
, "Failed to get device of root file system: %m");
3427 r
= get_block_device("/var", &b
);
3429 return log_error_errno(r
, "Failed to get device of /var/ file system: %m");
3431 /* if backing device is distinct, then measure /var/ too */
3438 STRV_FOREACH(p
, paths
) {
3439 _cleanup_free_
char *word
= NULL
, *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3440 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3442 r
= pcrextend_file_system_word(*p
, &word
, &normalized_path
);
3446 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3450 r
= make_pcrlock_record(TPM2_PCR_SYSTEM_IDENTITY
/* = 15 */, word
, SIZE_MAX
, &record
);
3454 r
= json_variant_new_array(&array
, &record
, 1);
3456 return log_error_errno(r
, "Failed to create record array: %m");
3458 r
= write_pcrlock(array
, pcrlock_file
);
3466 static int verb_unlock_file_system(int argc
, char *argv
[], void *userdata
) {
3467 const char* paths
[3] = {};
3477 STRV_FOREACH(p
, paths
) {
3478 _cleanup_free_
char *normalized_path
= NULL
, *pcrlock_file
= NULL
;
3480 r
= chase(*p
, NULL
, 0, &normalized_path
, NULL
);
3482 return log_error_errno(r
, "Failed to normal path '%s': %m", argv
[1]);
3484 r
= pcrlock_file_system_path(normalized_path
, &pcrlock_file
);
3488 r
= unlink_pcrlock(pcrlock_file
);
3496 static int verb_lock_pe(int argc
, char *argv
[], void *userdata
) {
3497 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
3498 _cleanup_close_
int fd
= -EBADF
;
3501 // FIXME: Maybe also generate a matching EV_EFI_VARIABLE_AUTHORITY records here for each signature that
3502 // covers this PE plus its hash, as alternatives under the same component name
3505 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3507 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3510 if (arg_pcr_mask
== 0)
3511 arg_pcr_mask
= UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE
;
3513 for (uint32_t i
= 0; i
< TPM2_PCRS_MAX
; i
++) {
3514 _cleanup_(json_variant_unrefp
) JsonVariant
*digests
= NULL
;
3516 if (!FLAGS_SET(arg_pcr_mask
, UINT32_C(1) << i
))
3519 FOREACH_ARRAY(pa
, tpm2_hash_algorithms
, TPM2_N_HASH_ALGORITHMS
) {
3520 _cleanup_free_
void *hash
= NULL
;
3525 assert_se(a
= tpm2_hash_alg_to_string(*pa
));
3526 assert_se(md
= EVP_get_digestbyname(a
));
3528 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &hash
, &hash_size
);
3530 return log_error_errno(r
, "Failed to hash PE binary: %m");
3532 r
= json_variant_append_arrayb(&digests
,
3534 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3535 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_size
))));
3537 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3540 r
= json_variant_append_arrayb(
3543 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i
)),
3544 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests
))));
3546 return log_error_errno(r
, "Failed to append record object: %m");
3549 return write_pcrlock(array
, NULL
);
3552 typedef void* SectionHashArray
[_UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
];
3554 static void section_hashes_array_done(SectionHashArray
*array
) {
3557 for (size_t i
= 0; i
< _UNIFIED_SECTION_MAX
* TPM2_N_HASH_ALGORITHMS
; i
++)
3561 static int verb_lock_uki(int argc
, char *argv
[], void *userdata
) {
3562 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
, *pe_digests
= NULL
;
3563 _cleanup_(section_hashes_array_done
) SectionHashArray section_hashes
= {};
3564 size_t hash_sizes
[TPM2_N_HASH_ALGORITHMS
];
3565 _cleanup_close_
int fd
= -EBADF
;
3568 if (arg_pcr_mask
!= 0)
3569 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "PCR not configurable for UKI lock down.");
3572 fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
);
3574 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3577 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3578 _cleanup_free_
void *peh
= NULL
;
3582 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3583 assert_se(md
= EVP_get_digestbyname(a
));
3585 r
= pe_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, &peh
, hash_sizes
+ i
);
3587 return log_error_errno(r
, "Failed to hash PE binary: %m");
3589 r
= json_variant_append_arrayb(
3592 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3593 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(peh
, hash_sizes
[i
]))));
3595 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3597 r
= uki_hash(fd
< 0 ? STDIN_FILENO
: fd
, md
, section_hashes
+ (i
* _UNIFIED_SECTION_MAX
), hash_sizes
+ i
);
3599 return log_error_errno(r
, "Failed to UKI hash PE binary: %m");
3602 r
= json_variant_append_arrayb(
3605 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_BOOT_LOADER_CODE
)),
3606 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(pe_digests
))));
3608 return log_error_errno(r
, "Failed to append record object: %m");
3610 for (UnifiedSection section
= 0; section
< _UNIFIED_SECTION_MAX
; section
++) {
3611 _cleanup_(json_variant_unrefp
) JsonVariant
*section_digests
= NULL
, *record
= NULL
;
3613 if (!unified_section_measure(section
))
3616 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3620 hash
= section_hashes
[i
* _UNIFIED_SECTION_MAX
+ section
];
3624 assert_se(a
= tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]));
3626 r
= json_variant_append_arrayb(
3629 JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a
)),
3630 JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash
, hash_sizes
[i
]))));
3632 return log_error_errno(r
, "Failed to build JSON digest object: %m");
3635 if (!section_digests
)
3638 /* So we have digests for this section, hence generate a record for the section name first. */
3639 r
= make_pcrlock_record(TPM2_PCR_KERNEL_BOOT
/* =11 */, unified_sections
[section
], strlen(unified_sections
[section
]) + 1, &record
);
3643 r
= json_variant_append_array(&array
, record
);
3645 return log_error_errno(r
, "Failed to append JSON record array: %m");
3647 /* And then append a record for the section contents digests as well */
3648 r
= json_variant_append_arrayb(
3651 JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(TPM2_PCR_KERNEL_BOOT
/* =11 */)),
3652 JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(section_digests
))));
3654 return log_error_errno(r
, "Failed to append record object: %m");
3657 return write_pcrlock(array
, NULL
);
3660 static int event_log_reduce_to_safe_pcrs(EventLog
*el
, uint32_t *pcrs
) {
3661 _cleanup_free_
char *dropped
= NULL
, *kept
= NULL
;
3666 /* When we compile a new PCR policy we don't want to bind to PCRs which are fishy for one of three
3669 * 1. The PCR value doesn't match the event log
3670 * 2. The event log for the PCR contains measurements we don't know responsible components for
3671 * 3. The event log for the PCR does not contain measurements for components we know
3673 * This function checks for the three conditions and drops the PCR from the mask.
3676 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
3678 if (!FLAGS_SET(*pcrs
, UINT32_C(1) << pcr
))
3681 if (!event_log_pcr_checks_out(el
, el
->registers
+ pcr
)) {
3682 log_notice("PCR %" PRIu32
" (%s) value does not match event log. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3686 if (!el
->registers
[pcr
].fully_recognized
) {
3687 log_notice("PCR %" PRIu32
" (%s) event log contains unrecognized measurements. Removing from set of PCRs.", pcr
, strna(tpm2_pcr_index_to_string(pcr
)));
3691 if (FLAGS_SET(el
->missing_component_pcrs
, UINT32_C(1) << pcr
)) {
3692 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
)));
3696 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
)));
3698 if (strextendf_with_separator(&kept
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3704 *pcrs
&= ~(UINT32_C(1) << pcr
);
3706 if (strextendf_with_separator(&dropped
, ", ", "%" PRIu32
" (%s)", pcr
, tpm2_pcr_index_to_string(pcr
)) < 0)
3711 log_notice("PCRs dropped from protection mask: %s", dropped
);
3713 log_debug("No PCRs dropped from protection mask.");
3716 log_notice("PCRs in protection mask: %s", kept
);
3718 log_notice("No PCRs kept in protection mask.");
3723 static int verb_lock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3724 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3725 _cleanup_free_
char *cmdline
= NULL
;
3729 if (empty_or_dash(argv
[1]))
3730 r
= read_full_stream(stdin
, &cmdline
, NULL
);
3732 r
= read_full_file(argv
[1], &cmdline
, NULL
);
3734 r
= proc_cmdline(&cmdline
);
3736 return log_error_errno(r
, "Failed to read cmdline: %m");
3738 delete_trailing_chars(cmdline
, "\n");
3740 _cleanup_free_ char16_t
*u
= NULL
;
3741 u
= utf8_to_utf16(cmdline
, SIZE_MAX
);
3745 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, u
, char16_strlen(u
)*2+2, &record
);
3749 r
= json_variant_new_array(&array
, &record
, 1);
3751 return log_error_errno(r
, "Failed to create record array: %m");
3753 r
= write_pcrlock(array
, PCRLOCK_KERNEL_CMDLINE_PATH
);
3760 static int verb_unlock_kernel_cmdline(int argc
, char *argv
[], void *userdata
) {
3761 return unlink_pcrlock(PCRLOCK_KERNEL_CMDLINE_PATH
);
3764 static int verb_lock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3765 _cleanup_(json_variant_unrefp
) JsonVariant
*record
= NULL
, *array
= NULL
;
3766 _cleanup_free_
void *data
= NULL
;
3767 _cleanup_fclose_
FILE *f
= NULL
;
3772 f
= fopen(argv
[1], "re");
3774 return log_error_errno(errno
, "Failed to open '%s': %m", argv
[1]);
3777 r
= read_full_stream(f
?: stdin
, (char**) &data
, &size
);
3779 return log_error_errno(r
, "Failed to read data from stdin: %m");
3781 r
= make_pcrlock_record(TPM2_PCR_KERNEL_INITRD
/* = 9 */, data
, size
, &record
);
3785 r
= json_variant_new_array(&array
, &record
, 1);
3787 return log_error_errno(r
, "Failed to create record array: %m");
3789 r
= write_pcrlock(array
, PCRLOCK_KERNEL_INITRD_PATH
);
3796 static int verb_unlock_kernel_initrd(int argc
, char *argv
[], void *userdata
) {
3797 return unlink_pcrlock(PCRLOCK_KERNEL_INITRD_PATH
);
3800 static int pcr_prediction_add_result(
3801 Tpm2PCRPrediction
*context
,
3802 Tpm2PCRPredictionResult
*result
,
3807 _cleanup_free_ Tpm2PCRPredictionResult
*copy
= NULL
;
3813 copy
= newdup(Tpm2PCRPredictionResult
, result
, 1);
3817 r
= ordered_set_ensure_put(context
->results
+ pcr
, &tpm2_pcr_prediction_result_hash_ops
, copy
);
3818 if (r
== -EEXIST
) /* Multiple identical results for the same PCR are totally expected */
3821 return log_error_errno(r
, "Failed to insert result into set: %m");
3823 log_debug("Added prediction result %u for PCR %" PRIu32
" (path: %s)", ordered_set_size(context
->results
[pcr
]), pcr
, strempty(path
));
3829 static const EVP_MD
* evp_from_tpm2_alg(uint16_t alg
) {
3832 name
= tpm2_hash_alg_to_string(alg
);
3836 return EVP_get_digestbyname(name
);
3839 static int event_log_component_variant_calculate(
3840 Tpm2PCRPrediction
*context
,
3841 Tpm2PCRPredictionResult
*result
,
3842 EventLogComponent
*component
,
3843 EventLogComponentVariant
*variant
,
3854 FOREACH_ARRAY(rr
, variant
->records
, variant
->n_records
) {
3855 EventLogRecord
*rec
= *rr
;
3857 if (rec
->pcr
!= pcr
)
3860 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
3861 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*md_ctx
= NULL
;
3862 EventLogRecordBank
*b
;
3864 if (result
->hash
[i
].size
<= 0) /* already invalidated */
3867 b
= event_log_record_find_bank(rec
, tpm2_hash_algorithms
[i
]);
3869 /* Can't calculate, hence invalidate */
3870 result
->hash
[i
] = (TPM2B_DIGEST
) {};
3874 md_ctx
= EVP_MD_CTX_new();
3878 const EVP_MD
*md
= ASSERT_PTR(evp_from_tpm2_alg(tpm2_hash_algorithms
[i
]));
3880 int sz
= EVP_MD_size(md
);
3882 assert((size_t) sz
<= sizeof_field(TPM2B_DIGEST
, buffer
));
3884 assert(sz
== tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]));
3886 assert(result
->hash
[i
].size
== (size_t) sz
);
3887 assert(b
->hash
.size
== (size_t) sz
);
3889 if (EVP_DigestInit_ex(md_ctx
, md
, NULL
) != 1)
3890 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to initialize message digest.");
3892 if (EVP_DigestUpdate(md_ctx
, result
->hash
[i
].buffer
, sz
) != 1)
3893 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash bank value.");
3895 if (EVP_DigestUpdate(md_ctx
, b
->hash
.buffer
, sz
) != 1)
3896 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to hash data value.");
3898 unsigned l
= (unsigned) sz
;
3899 if (EVP_DigestFinal_ex(md_ctx
, result
->hash
[i
].buffer
, &l
) != 1)
3900 return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE
), "Failed to finalize message digest.");
3902 assert(l
== (unsigned) sz
);
3905 /* This is a valid result once we hit the start location */
3906 if (arg_location_start
&& strcmp(component
->id
, arg_location_start
) >= 0) {
3907 r
= pcr_prediction_add_result(context
, result
, pcr
, path
, rr
- variant
->records
);
3916 static int event_log_predict_pcrs(
3918 Tpm2PCRPrediction
*context
,
3919 Tpm2PCRPredictionResult
*parent_result
,
3920 size_t component_index
,
3924 EventLogComponent
*component
;
3929 assert(parent_result
);
3931 /* Check if we reached the end of the components, generate a result, and backtrack */
3932 if (component_index
>= el
->n_components
||
3933 (arg_location_end
&& strcmp(el
->components
[component_index
]->id
, arg_location_end
) > 0)) {
3934 r
= pcr_prediction_add_result(context
, parent_result
, pcr
, path
, /* offset= */ 0);
3941 component
= ASSERT_PTR(el
->components
[component_index
]);
3943 FOREACH_ARRAY(ii
, component
->variants
, component
->n_variants
) {
3944 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
3945 EventLogComponentVariant
*variant
= *ii
;
3946 _cleanup_free_
char *subpath
= NULL
;
3948 /* Operate on a copy of the result */
3951 subpath
= strjoin(path
, ":", component
->id
);
3953 subpath
= strdup(component
->id
);
3957 if (!streq(component
->id
, variant
->id
))
3958 if (!strextend(&subpath
, "@", variant
->id
))
3961 result
= newdup(Tpm2PCRPredictionResult
, parent_result
, 1);
3965 r
= event_log_component_variant_calculate(
3975 r
= event_log_predict_pcrs(
3979 component_index
+ 1, /* Next component */
3991 static ssize_t
event_log_calculate_component_combinations(EventLog
*el
) {
3995 FOREACH_ARRAY(cc
, el
->components
, el
->n_components
) {
3996 EventLogComponent
*c
= *cc
;
3998 /* Overflow check */
3999 if (c
->n_variants
> (size_t) (SSIZE_MAX
/count
))
4000 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Too many component combinations.");
4002 count
*= c
->n_variants
;
4008 static int event_log_show_predictions(Tpm2PCRPrediction
*context
, uint16_t alg
) {
4013 pager_open(arg_pager_flags
);
4015 if (!FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
4016 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
4018 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++) {
4019 _cleanup_(json_variant_unrefp
) JsonVariant
*aj
= NULL
;
4021 r
= tpm2_pcr_prediction_to_json(
4023 tpm2_hash_algorithms
[i
],
4028 if (json_variant_elements(aj
) == 0)
4031 r
= json_variant_set_field(
4033 tpm2_hash_alg_to_string(tpm2_hash_algorithms
[i
]),
4036 return log_error_errno(r
, "Failed to add prediction bank to object: %m");
4040 r
= json_variant_new_object(&j
, NULL
, 0);
4042 return log_error_errno(r
, "Failed to allocated empty object: %m");
4045 json_variant_dump(j
, arg_json_format_flags
, /* f= */ NULL
, /* prefix= */ NULL
);
4049 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4050 Tpm2PCRPredictionResult
*result
;
4051 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4054 if (ordered_set_isempty(context
->results
[pcr
])) {
4055 printf("No results for PCR %u (%s).\n", pcr
, tpm2_pcr_index_to_string(pcr
));
4059 printf("%sResults for PCR %u (%s):%s\n", ansi_underline(), pcr
, tpm2_pcr_index_to_string(pcr
), ansi_normal());
4061 ORDERED_SET_FOREACH(result
, context
->results
[pcr
]) {
4063 _cleanup_free_
char *aa
= NULL
, *h
= NULL
;
4066 TPM2B_DIGEST
*hash
= tpm2_pcr_prediction_result_get_hash(result
, alg
);
4070 a
= ASSERT_PTR(tpm2_hash_alg_to_string(alg
));
4077 h
= hexmem(hash
->buffer
, hash
->size
);
4081 printf(" %s%-6s:%s %s\n", ansi_grey(), aa
, ansi_normal(), h
);
4088 static int tpm2_pcr_prediction_run(
4090 Tpm2PCRPrediction
*context
) {
4097 for (uint32_t pcr
= 0; pcr
< TPM2_PCRS_MAX
; pcr
++) {
4098 _cleanup_free_ Tpm2PCRPredictionResult
*result
= NULL
;
4100 if (!FLAGS_SET(context
->pcrs
, UINT32_C(1) << pcr
))
4103 result
= new0(Tpm2PCRPredictionResult
, 1);
4107 for (size_t i
= 0; i
< TPM2_N_HASH_ALGORITHMS
; i
++)
4108 event_log_initial_pcr_state(el
, pcr
, tpm2_hash_alg_to_size(tpm2_hash_algorithms
[i
]), result
->hash
+ i
);
4110 r
= event_log_predict_pcrs(
4114 /* component_index= */ 0,
4124 static int verb_predict(int argc
, char *argv
[], void *userdata
) {
4125 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction context
= {
4126 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4128 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4132 r
= event_log_load_and_process(&el
);
4136 count
= event_log_calculate_component_combinations(el
);
4140 log_info("%zi combinations of components.", count
);
4142 r
= event_log_reduce_to_safe_pcrs(el
, &context
.pcrs
);
4146 r
= tpm2_pcr_prediction_run(el
, &context
);
4150 return event_log_show_predictions(&context
, el
->primary_algorithm
);
4153 static int remove_policy_file(const char *path
) {
4156 if (unlink(path
) < 0) {
4157 if (errno
== ENOENT
)
4160 return log_error_errno(errno
, "Failed to remove policy file '%s': %m", path
);
4163 log_info("Removed policy file '%s'.", path
);
4167 static int verb_make_policy(int argc
, char *argv
[], void *userdata
) {
4170 /* Here's how this all works: after predicting all possible PCR values for next boot (with
4171 * alternatives) we'll calculate a policy from it as a combination of PolicyPCR + PolicyOR
4172 * expressions. This is then stored in an NV index. When a component of the boot process is changed a
4173 * new prediction is made and the NV index updated (which automatically invalidates any older
4176 * Whenever we want to lock an encrypted object (for example FDE) against this policy, we'll use a
4177 * PolicyAuthorizeNV epxression that pins the NV index in the policy, and permits access to any
4178 * policies matching the current NV index contents.
4180 * We grant world-readable read access to the NV index. Write access is controlled by a PIN (which we
4181 * either generate locally or which the user can provide us with) which can also be used for
4182 * recovery. This PIN is sealed to the TPM and is locked via PolicyAuthorizeNV to the NV index it
4183 * protects (i.e. we dogfood 🌭 🐶 hard here). This means in order to update such a policy we need
4184 * the policy to pass.
4186 * Information about the used NV Index, the SRK of the TPM, the sealed PIN and the current PCR
4187 * prediction data are stored in a JSON file in /var/lib/. In order to be able to unlock root disks
4188 * this data must be also copied to the ESP so that it is available to the initrd. The data is not
4189 * sensitive, as SRK and NV index are pinned by it, and the prediction data must match the NV index
4192 usec_t start_usec
= now(CLOCK_MONOTONIC
);
4194 _cleanup_(event_log_freep
) EventLog
*el
= NULL
;
4195 r
= event_log_load_and_process(&el
);
4199 _cleanup_(tpm2_pcr_prediction_done
) Tpm2PCRPrediction new_prediction
= {
4200 arg_pcr_mask
!= 0 ? arg_pcr_mask
: DEFAULT_PCR_MASK
,
4202 r
= event_log_reduce_to_safe_pcrs(el
, &new_prediction
.pcrs
);
4206 usec_t predict_start_usec
= now(CLOCK_MONOTONIC
);
4208 r
= tpm2_pcr_prediction_run(el
, &new_prediction
);
4212 log_info("Predicted future PCRs in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), predict_start_usec
), 1));
4214 _cleanup_(json_variant_unrefp
) JsonVariant
*new_prediction_json
= NULL
;
4215 r
= tpm2_pcr_prediction_to_json(&new_prediction
, el
->primary_algorithm
, &new_prediction_json
);
4220 (void) json_variant_dump(new_prediction_json
, JSON_FORMAT_PRETTY_AUTO
|JSON_FORMAT_COLOR_AUTO
, stderr
, NULL
);
4222 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy old_policy
= {};
4224 r
= tpm2_pcrlock_policy_load(arg_pcrlock_path
, &old_policy
);
4228 bool have_old_policy
= r
> 0;
4230 /* When we update the policy the old serializations for NV, SRK, PIN remain the same */
4231 _cleanup_(iovec_done
) struct iovec
4232 nv_blob
= TAKE_STRUCT(old_policy
.nv_handle
),
4233 nv_public_blob
= TAKE_STRUCT(old_policy
.nv_public
),
4234 srk_blob
= TAKE_STRUCT(old_policy
.srk_handle
),
4235 pin_public
= TAKE_STRUCT(old_policy
.pin_public
),
4236 pin_private
= TAKE_STRUCT(old_policy
.pin_private
);
4238 if (have_old_policy
) {
4239 if (arg_nv_index
!= 0 && old_policy
.nv_index
!= arg_nv_index
)
4240 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
);
4243 old_policy
.algorithm
== el
->primary_algorithm
&&
4244 tpm2_pcr_prediction_equal(&old_policy
.prediction
, &new_prediction
, el
->primary_algorithm
)) {
4245 log_info("Prediction is identical to current policy, skipping update.");
4246 return EXIT_SUCCESS
;
4250 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4251 r
= tpm2_context_new(NULL
, &tc
);
4253 return log_error_errno(r
, "Failed to allocate TPM2 context: %m");
4255 if (!tpm2_supports_command(tc
, TPM2_CC_PolicyAuthorizeNV
))
4256 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "TPM2 does not support PolicyAuthorizeNV command, refusing.");
4258 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4260 if (iovec_is_set(&srk_blob
)) {
4261 r
= tpm2_deserialize(
4267 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4269 r
= tpm2_get_or_create_srk(
4271 /* session= */ NULL
,
4272 /* ret_public= */ NULL
,
4273 /* ret_name= */ NULL
,
4274 /* ret_qname= */ NULL
,
4277 return log_error_errno(r
, "Failed to install SRK: %m");
4280 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4281 r
= tpm2_make_encryption_session(
4284 /* bind_key= */ &TPM2_HANDLE_NONE
,
4285 &encryption_session
);
4287 return log_error_errno(r
, "Failed to allocate encryption session: %m");
4289 /* Acquire a recovery PIN, either from the user, or create a randomized one */
4290 _cleanup_(erase_and_freep
) char *pin
= NULL
;
4291 if (arg_recovery_pin
) {
4292 r
= getenv_steal_erase("PIN", &pin
);
4294 return log_error_errno(r
, "Failed to acquire PIN from environment: %m");
4296 _cleanup_(strv_free_erasep
) char **l
= NULL
;
4298 r
= ask_password_auto(
4301 /* id= */ "pcrlock-recovery-pin",
4302 /* key_name= */ NULL
,
4303 /* credential_name= */ "systemd-pcrlock.recovery-pin",
4308 return log_error_errno(r
, "Failed to query for recovery PIN: %m");
4310 if (strv_length(l
) != 1)
4311 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expected a single PIN only.");
4313 pin
= TAKE_PTR(l
[0]);
4317 } else if (!have_old_policy
) {
4320 r
= crypto_random_bytes(rnd
, sizeof(rnd
));
4322 return log_error_errno(r
, "Failed to generate a randomized recovery PIN: %m");
4324 (void) base64mem(rnd
, sizeof(rnd
), &pin
);
4325 explicit_bzero_safe(rnd
, sizeof(rnd
));
4330 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4331 TPM2_HANDLE nv_index
= 0;
4333 if (iovec_is_set(&nv_blob
)) {
4334 r
= tpm2_deserialize(tc
, nv_blob
.iov_base
, nv_blob
.iov_len
, &nv_handle
);
4336 return log_error_errno(r
, "Failed to deserialize NV index TR: %m");
4338 nv_index
= old_policy
.nv_index
;
4341 TPM2B_AUTH auth
= {};
4342 CLEANUP_ERASE(auth
);
4345 r
= tpm2_get_pin_auth(TPM2_ALG_SHA256
, pin
, &auth
);
4347 return log_error_errno(r
, "Failed to hash PIN: %m");
4349 assert(iovec_is_set(&pin_public
));
4350 assert(iovec_is_set(&pin_private
));
4352 log_debug("Retrieving PIN from sealed data.");
4354 usec_t pin_start_usec
= now(CLOCK_MONOTONIC
);
4356 _cleanup_(iovec_done_erase
) struct iovec secret
= {};
4357 for (unsigned attempt
= 0;; attempt
++) {
4358 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4360 r
= tpm2_make_policy_session(
4366 return log_error_errno(r
, "Failed to allocate policy session: %m");
4368 r
= tpm2_policy_super_pcr(
4371 &old_policy
.prediction
,
4372 old_policy
.algorithm
);
4374 return log_error_errno(r
, "Failed to submit super PCR policy: %m");
4376 r
= tpm2_policy_authorize_nv(
4382 return log_error_errno(r
, "Failed to submit AuthorizeNV policy: %m");
4384 r
= tpm2_unseal_data(
4392 if (r
< 0 && (r
!= -ESTALE
|| attempt
>= 16))
4393 return log_error_errno(r
, "Failed to unseal PIN: %m");
4397 log_debug("Trying again (attempt %u), as PCR values changed during unlock attempt.", attempt
+1);
4400 if (secret
.iov_len
> sizeof_field(TPM2B_AUTH
, buffer
))
4401 return log_error_errno(SYNTHETIC_ERRNO(E2BIG
), "Decrypted PIN too large.");
4403 auth
= (TPM2B_AUTH
) {
4404 .size
= secret
.iov_len
,
4407 memcpy_safe(auth
.buffer
, secret
.iov_base
, secret
.iov_len
);
4409 log_info("Retrieved PIN from TPM2 in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_start_usec
), 1));
4412 TPM2B_NV_PUBLIC nv_public
= {};
4414 usec_t nv_index_start_usec
= now(CLOCK_MONOTONIC
);
4416 if (!iovec_is_set(&nv_blob
)) {
4417 TPM2B_DIGEST recovery_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4418 r
= tpm2_calculate_policy_auth_value(&recovery_policy_digest
);
4420 return log_error_errno(r
, "Failed to calculate authentication value policy: %m");
4422 log_debug("Allocating NV index to write PCR policy to...");
4423 r
= tpm2_define_policy_nv_index(
4427 &recovery_policy_digest
,
4434 return log_error_errno(r
, "NV index 0x%" PRIx32
" already allocated.", arg_nv_index
);
4436 return log_error_errno(r
, "Failed to allocate NV index: %m");
4439 r
= tpm2_set_auth_binary(tc
, nv_handle
, &auth
);
4441 return log_error_errno(r
, "Failed to set authentication value on NV index: %m");
4443 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*policy_session
= NULL
;
4444 r
= tpm2_make_policy_session(
4450 return log_error_errno(r
, "Failed to allocate policy session: %m");
4452 r
= tpm2_policy_auth_value(
4455 /* ret_policy_digest= */ NULL
);
4457 return log_error_errno(r
, "Failed to submit authentication value policy: %m");
4459 log_debug("Calculating new PCR policy to write...");
4460 TPM2B_DIGEST new_super_pcr_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4462 usec_t pcr_policy_start_usec
= now(CLOCK_MONOTONIC
);
4464 r
= tpm2_calculate_policy_super_pcr(
4466 el
->primary_algorithm
,
4467 &new_super_pcr_policy_digest
);
4469 return log_error_errno(r
, "Failed to calculate super PCR policy: %m");
4471 log_info("Calculated new PCR policy in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pcr_policy_start_usec
), 1));
4473 log_debug("Writing new PCR policy to NV index...");
4474 r
= tpm2_write_policy_nv_index(
4479 &new_super_pcr_policy_digest
);
4481 return log_error_errno(r
, "Failed to write to NV index: %m");
4483 log_info("Updated NV index in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), nv_index_start_usec
), 1));
4485 assert(iovec_is_set(&pin_public
) == iovec_is_set(&pin_private
));
4486 if (!iovec_is_set(&pin_public
)) {
4487 TPM2B_DIGEST authnv_policy_digest
= TPM2B_DIGEST_MAKE(NULL
, TPM2_SHA256_DIGEST_SIZE
);
4489 r
= tpm2_calculate_policy_authorize_nv(&nv_public
, &authnv_policy_digest
);
4491 return log_error_errno(r
, "Failed to calculate AuthorizeNV policy: %m");
4493 struct iovec data
= {
4494 .iov_base
= auth
.buffer
,
4495 .iov_len
= auth
.size
,
4498 usec_t pin_seal_start_usec
= now(CLOCK_MONOTONIC
);
4500 log_debug("Sealing PIN to NV index policy...");
4506 &authnv_policy_digest
,
4510 return log_error_errno(r
, "Failed to seal PIN to NV auth policy: %m");
4512 log_info("Sealed PIN in %s.", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), pin_seal_start_usec
), 1));
4515 if (!iovec_is_set(&nv_blob
)) {
4516 r
= tpm2_serialize(tc
, nv_handle
, &nv_blob
.iov_base
, &nv_blob
.iov_len
);
4518 return log_error_errno(r
, "Failed to serialize NV index TR: %m");
4521 if (!iovec_is_set(&srk_blob
)) {
4522 r
= tpm2_serialize(tc
, srk_handle
, &srk_blob
.iov_base
, &srk_blob
.iov_len
);
4524 return log_error_errno(r
, "Failed to serialize SRK index TR: %m");
4527 if (!iovec_is_set(&nv_public_blob
)) {
4528 r
= tpm2_marshal_nv_public(&nv_public
, &nv_public_blob
.iov_base
, &nv_public_blob
.iov_len
);
4530 return log_error_errno(r
, "Failed to marshal NV public area: %m");
4533 _cleanup_(json_variant_unrefp
) JsonVariant
*new_configuration_json
= NULL
;
4534 r
= json_build(&new_configuration_json
,
4536 JSON_BUILD_PAIR_STRING("pcrBank", tpm2_hash_alg_to_string(el
->primary_algorithm
)),
4537 JSON_BUILD_PAIR_VARIANT("pcrValues", new_prediction_json
),
4538 JSON_BUILD_PAIR_INTEGER("nvIndex", nv_index
),
4539 JSON_BUILD_PAIR_IOVEC_BASE64("nvHandle", &nv_blob
),
4540 JSON_BUILD_PAIR_IOVEC_BASE64("nvPublic", &nv_public_blob
),
4541 JSON_BUILD_PAIR_IOVEC_BASE64("srkHandle", &srk_blob
),
4542 JSON_BUILD_PAIR_IOVEC_BASE64("pinPublic", &pin_public
),
4543 JSON_BUILD_PAIR_IOVEC_BASE64("pinPrivate", &pin_private
)));
4545 return log_error_errno(r
, "Failed to generate JSON: %m");
4547 _cleanup_free_
char *text
= NULL
;
4548 r
= json_variant_format(new_configuration_json
, 0, &text
);
4550 return log_error_errno(r
, "Failed to format new configuration to JSON: %m");
4552 const char *path
= arg_pcrlock_path
?: (in_initrd() ? "/run/systemd/pcrlock.json" : "/var/lib/systemd/pcrlock.json");
4553 r
= write_string_file(path
, text
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_SYNC
|WRITE_STRING_FILE_MKDIR_0755
);
4555 return log_error_errno(r
, "Failed to write new configuration to '%s': %m", path
);
4557 if (!arg_pcrlock_path
&& !in_initrd()) {
4558 r
= remove_policy_file("/run/systemd/pcrlock.json");
4563 log_info("Written new policy to '%s' and digest to TPM2 NV index 0x%" PRIu32
".", path
, nv_index
);
4565 log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC
), start_usec
), 1));
4570 static int undefine_policy_nv_index(
4572 const struct iovec
*nv_blob
,
4573 const struct iovec
*srk_blob
) {
4579 _cleanup_(tpm2_context_unrefp
) Tpm2Context
*tc
= NULL
;
4580 r
= tpm2_context_new(NULL
, &tc
);
4584 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*srk_handle
= NULL
;
4585 r
= tpm2_deserialize(
4591 return log_error_errno(r
, "Failed to deserialize SRK TR: %m");
4593 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*nv_handle
= NULL
;
4594 r
= tpm2_deserialize(
4600 return log_error_errno(r
, "Failed to deserialize NV TR: %m");
4602 _cleanup_(tpm2_handle_freep
) Tpm2Handle
*encryption_session
= NULL
;
4603 r
= tpm2_make_encryption_session(
4606 /* bind_key= */ &TPM2_HANDLE_NONE
,
4607 &encryption_session
);
4611 r
= tpm2_undefine_policy_nv_index(
4619 log_info("Removed NV index 0x%x", nv_index
);
4623 static int verb_remove_policy(int argc
, char *argv
[], void *userdata
) {
4626 _cleanup_(tpm2_pcrlock_policy_done
) Tpm2PCRLockPolicy policy
= {};
4627 r
= tpm2_pcrlock_policy_load(arg_policy_path
, &policy
);
4629 log_info("No policy found.");
4634 log_notice("Failed to load old policy file, assuming it is corrupted, removing.");
4636 r
= undefine_policy_nv_index(policy
.nv_index
, &policy
.nv_handle
, &policy
.srk_handle
);
4638 log_notice("Failed to remove NV index, assuming data out of date, removing policy file.");
4641 if (arg_policy_path
) {
4642 r
= remove_policy_file(arg_policy_path
);
4650 RET_GATHER(ret
, remove_policy_file("/var/lib/systemd/pcrlock.json"));
4651 RET_GATHER(ret
, remove_policy_file("/run/systemd/pcrlock.json"));
4657 static int help(int argc
, char *argv
[], void *userdata
) {
4658 _cleanup_free_
char *link
= NULL
;
4661 r
= terminal_urlify_man("systemd-pcrlock", "8", &link
);
4665 printf("%1$s [OPTIONS...] COMMAND ...\n"
4666 "\n%5$sManage a TPM2 PCR lock.%6$s\n"
4667 "\n%3$sCommands:%4$s\n"
4668 " log Show measurement log\n"
4669 " cel Show measurement log in TCG CEL-JSON format\n"
4670 " list-components List defined .pcrlock components\n"
4671 " predict Predict PCR values\n"
4672 " make-policy Predict PCR values and generate TPM2 policy from it\n"
4673 " remove-policy Remove TPM2 policy\n"
4674 "\n%3$sProtections:%4$s\n"
4675 " lock-firmware-code Generate a .pcrlock file from current firmware code\n"
4676 " unlock-firmware-code Remove .pcrlock file for firmware code\n"
4677 " lock-firmware-config Generate a .pcrlock file from current firmware configuration\n"
4678 " unlock-firmware-config Remove .pcrlock file for firmware configuration\n"
4679 " lock-secureboot-policy Generate a .pcrlock file from current SecureBoot policy\n"
4680 " unlock-secureboot-policy Remove .pcrlock file for SecureBoot policy\n"
4681 " lock-secureboot-authority Generate a .pcrlock file from current SecureBoot authority\n"
4682 " unlock-secureboot-authority Remove .pcrlock file for SecureBoot authority\n"
4683 " lock-gpt [DISK] Generate a .pcrlock file from GPT header\n"
4684 " unlock-gpt Remove .pcrlock file for GPT header\n"
4685 " lock-pe [BINARY] Generate a .pcrlock file from PE binary\n"
4686 " unlock-pe Remove .pcrlock file for PE binary\n"
4687 " lock-uki [UKI] Generate a .pcrlock file from UKI PE binary\n"
4688 " unlock-uki Remove .pcrlock file for UKI PE binary\n"
4689 " lock-machine-id Generate a .pcrlock file from current machine ID\n"
4690 " unlock-machine-id Remove .pcrlock file for machine ID\n"
4691 " lock-file-system [PATH] Generate a .pcrlock file from current root fs + /var/\n"
4692 " unlock-file-system [PATH] Remove .pcrlock file for root fs + /var/\n"
4693 " lock-kernel-cmdline [FILE] Generate a .pcrlock file from kernel command line\n"
4694 " unlock-kernel-cmdline Remove .pcrlock file for kernel command line\n"
4695 " lock-kernel-initrd FILE Generate a .pcrlock file from an initrd file\n"
4696 " unlock-kernel-initrd Remove .pcrlock file for an initrd file\n"
4697 " lock-raw [FILE] Generate a .pcrlock file from raw data\n"
4698 " unlock-raw Remove .pcrlock file for raw data\n"
4699 "\n%3$sOptions:%4$s\n"
4700 " -h --help Show this help\n"
4701 " --version Print version\n"
4702 " --no-pager Do not pipe output into a pager\n"
4703 " --json=pretty|short|off Generate JSON output\n"
4704 " --raw-description Show raw firmware record data as description in table\n"
4705 " --pcr=NR Generate .pcrlock for specified PCR\n"
4706 " --nv-index=NUMBER Use the specified NV index, instead of a random one\n"
4707 " --components=PATH Directory to read .pcrlock files from\n"
4708 " --location=STRING[:STRING]\n"
4709 " Do not process components beyond this component name\n"
4710 " --recovery-pin=yes Ask for a recovery PIN\n"
4711 " --pcrlock=PATH .pcrlock file to write expected PCR measurement to\n"
4712 " --policy=PATH JSON file to write policy output to\n"
4713 " --force Write policy even if it matches existing policy\n"
4714 "\nSee the %2$s for details.\n",
4715 program_invocation_short_name
,
4725 static int parse_argv(int argc
, char *argv
[]) {
4727 ARG_VERSION
= 0x100,
4730 ARG_RAW_DESCRIPTION
,
4741 static const struct option options
[] = {
4742 { "help", no_argument
, NULL
, 'h' },
4743 { "version", no_argument
, NULL
, ARG_VERSION
},
4744 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
4745 { "json", required_argument
, NULL
, ARG_JSON
},
4746 { "raw-description", no_argument
, NULL
, ARG_RAW_DESCRIPTION
},
4747 { "pcr", required_argument
, NULL
, ARG_PCR
},
4748 { "nv-index", required_argument
, NULL
, ARG_NV_INDEX
},
4749 { "components", required_argument
, NULL
, ARG_COMPONENTS
},
4750 { "location", required_argument
, NULL
, ARG_LOCATION
},
4751 { "recovery-pin", required_argument
, NULL
, ARG_RECOVERY_PIN
},
4752 { "pcrlock", required_argument
, NULL
, ARG_PCRLOCK
},
4753 { "policy", required_argument
, NULL
, ARG_POLICY
},
4754 { "force", no_argument
, NULL
, ARG_FORCE
},
4758 bool auto_location
= true;
4764 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
4768 help(0, NULL
, NULL
);
4775 arg_pager_flags
|= PAGER_DISABLE
;
4779 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
4784 case ARG_RAW_DESCRIPTION
:
4785 arg_raw_description
= true;
4789 r
= tpm2_parse_pcr_argument_to_mask(optarg
, &arg_pcr_mask
);
4791 return log_error_errno(r
, "Failed to parse PCR specification: %s", optarg
);
4797 if (isempty(optarg
))
4802 r
= safe_atou32_full(optarg
, 16, &u
);
4804 return log_error_errno(r
, "Failed to parse --nv-index= argument: %s", optarg
);
4806 if (u
< TPM2_NV_INDEX_FIRST
|| u
> TPM2_NV_INDEX_LAST
)
4807 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Argument for --nv-index= outside of valid range 0x%" PRIx32
"…0x%" PRIx32
": 0x%" PRIx32
,
4808 TPM2_NV_INDEX_FIRST
, TPM2_NV_INDEX_LAST
, u
);
4814 case ARG_COMPONENTS
: {
4815 _cleanup_free_
char *p
= NULL
;
4817 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &p
);
4821 r
= strv_consume(&arg_components
, TAKE_PTR(p
));
4828 case ARG_LOCATION
: {
4829 _cleanup_free_
char *start
= NULL
, *end
= NULL
;
4832 auto_location
= false;
4834 if (isempty(optarg
)) {
4835 arg_location_start
= mfree(arg_location_start
);
4836 arg_location_end
= mfree(arg_location_end
);
4840 e
= strchr(optarg
, ':');
4842 start
= strndup(optarg
, e
- optarg
);
4846 end
= strdup(e
+ 1);
4850 start
= strdup(optarg
);
4854 end
= strdup(optarg
);
4859 if (!filename_is_valid(start
))
4860 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", start
);
4861 if (!filename_is_valid(end
))
4862 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Location string invalid, refusing: %s", end
);
4864 free_and_replace(arg_location_start
, start
);
4865 free_and_replace(arg_location_end
, end
);
4869 case ARG_RECOVERY_PIN
:
4870 r
= parse_boolean_argument("--recovery-pin", optarg
, &arg_recovery_pin
);
4876 if (empty_or_dash(optarg
))
4877 arg_pcrlock_path
= mfree(arg_pcrlock_path
);
4879 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_pcrlock_path
);
4884 arg_pcrlock_auto
= false;
4888 if (empty_or_dash(optarg
))
4889 arg_policy_path
= mfree(arg_policy_path
);
4891 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_policy_path
);
4906 assert_not_reached();
4909 if (auto_location
) {
4910 assert(!arg_location_start
);
4911 assert(!arg_location_end
);
4913 arg_location_start
= strdup("760-");
4914 if (!arg_location_start
)
4917 arg_location_end
= strdup("940-");
4918 if (!arg_location_end
)
4925 static int pcrlock_main(int argc
, char *argv
[]) {
4926 static const Verb verbs
[] = {
4927 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
4928 { "log", VERB_ANY
, 1, VERB_DEFAULT
, verb_show_log
},
4929 { "cel", VERB_ANY
, 1, 0, verb_show_cel
},
4930 { "list-components", VERB_ANY
, 1, 0, verb_list_components
},
4931 { "predict", VERB_ANY
, 1, 0, verb_predict
},
4932 { "lock-firmware-code", VERB_ANY
, 2, 0, verb_lock_firmware
},
4933 { "unlock-firmware-code", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4934 { "lock-firmware-config", VERB_ANY
, 2, 0, verb_lock_firmware
},
4935 { "unlock-firmware-config", VERB_ANY
, 1, 0, verb_unlock_firmware
},
4936 { "lock-secureboot-policy", VERB_ANY
, 1, 0, verb_lock_secureboot_policy
},
4937 { "unlock-secureboot-policy", VERB_ANY
, 1, 0, verb_unlock_secureboot_policy
},
4938 { "lock-secureboot-authority", VERB_ANY
, 1, 0, verb_lock_secureboot_authority
},
4939 { "unlock-secureboot-authority", VERB_ANY
, 1, 0, verb_unlock_secureboot_authority
},
4940 { "lock-gpt", VERB_ANY
, 2, 0, verb_lock_gpt
},
4941 { "unlock-gpt", VERB_ANY
, 1, 0, verb_unlock_gpt
},
4942 { "lock-pe", VERB_ANY
, 2, 0, verb_lock_pe
},
4943 { "unlock-pe", VERB_ANY
, 1, 0, verb_unlock_simple
},
4944 { "lock-uki", VERB_ANY
, 2, 0, verb_lock_uki
},
4945 { "unlock-uki", VERB_ANY
, 1, 0, verb_unlock_simple
},
4946 { "lock-machine-id", VERB_ANY
, 1, 0, verb_lock_machine_id
},
4947 { "unlock-machine-id", VERB_ANY
, 1, 0, verb_unlock_machine_id
},
4948 { "lock-file-system", VERB_ANY
, 2, 0, verb_lock_file_system
},
4949 { "unlock-file-system", VERB_ANY
, 2, 0, verb_unlock_file_system
},
4950 { "lock-kernel-cmdline", VERB_ANY
, 2, 0, verb_lock_kernel_cmdline
},
4951 { "unlock-kernel-cmdline", VERB_ANY
, 1, 0, verb_unlock_kernel_cmdline
},
4952 { "lock-kernel-initrd", VERB_ANY
, 2, 0, verb_lock_kernel_initrd
},
4953 { "unlock-kernel-initrd", VERB_ANY
, 1, 0, verb_unlock_kernel_initrd
},
4954 { "lock-raw", VERB_ANY
, 2, 0, verb_lock_raw
},
4955 { "unlock-raw", VERB_ANY
, 1, 0, verb_unlock_simple
},
4956 { "make-policy", VERB_ANY
, 1, 0, verb_make_policy
},
4957 { "remove-policy", VERB_ANY
, 1, 0, verb_remove_policy
},
4961 return dispatch_verb(argc
, argv
, verbs
, NULL
);
4964 static int run(int argc
, char *argv
[]) {
4967 log_show_color(true);
4968 log_parse_environment();
4971 r
= parse_argv(argc
, argv
);
4975 return pcrlock_main(argc
, argv
);
4978 DEFINE_MAIN_FUNCTION(run
);