From: Luca Boccassi Date: Tue, 30 Jun 2026 23:47:22 +0000 (+0100) Subject: pcrlock: add vl methods for fw and sb X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ef7b9f679b9e603a9d5eed7934daca0d61d581c5;p=thirdparty%2Fsystemd.git pcrlock: add vl methods for fw and sb Add varlink method wrappers for locking/unlocking firmware code and config, and secure boot policy and authority. --- diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 8b4ebceeb65..60891c1e660 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -4178,9 +4178,7 @@ static bool event_log_record_is_separator(const EventLogRecord *rec) { VERB_GROUP("Protections"); -VERB(verb_lock_firmware, "lock-firmware-code", NULL, VERB_ANY, 2, 0, - "Generate a .pcrlock file from current firmware code"); -static int verb_lock_firmware(int argc, char *argv[], uintptr_t _data, void *userdata) { +static int lock_firmware(bool is_code) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *array_early = NULL, *array_late = NULL; _cleanup_(event_log_freep) EventLog *el = NULL; uint32_t always_mask, separator_mask, separator_seen_mask = 0, action_seen_mask = 0; @@ -4193,7 +4191,7 @@ static int verb_lock_firmware(int argc, char *argv[], uintptr_t _data, void *use * here – but the latter only until the "separator" events are seen, which tell us where transition * into OS boot loader happens. This reflects the fact that on some systems the firmware already * measures some firmware-supplied apps into PCR 4. (e.g. Thinkpad X1 Gen9) */ - if (endswith(argv[0], "firmware-code")) { + if (is_code) { always_mask = (UINT32_C(1) << TPM2_PCR_PLATFORM_CODE) | /* → 0 */ (UINT32_C(1) << TPM2_PCR_EXTERNAL_CODE); /* → 2 */ @@ -4202,7 +4200,6 @@ static int verb_lock_firmware(int argc, char *argv[], uintptr_t _data, void *use default_pcrlock_early_path = PCRLOCK_FIRMWARE_CODE_EARLY_PATH; default_pcrlock_late_path = PCRLOCK_FIRMWARE_CODE_LATE_PATH; } else { - assert(endswith(argv[0], "firmware-config")); always_mask = (UINT32_C(1) << TPM2_PCR_PLATFORM_CONFIG) | /* → 1 */ (UINT32_C(1) << TPM2_PCR_EXTERNAL_CONFIG); /* → 3 */ @@ -4299,13 +4296,17 @@ static int verb_lock_firmware(int argc, char *argv[], uintptr_t _data, void *use return write_pcrlock(array_late, default_pcrlock_late_path); } -VERB_NOARG(verb_unlock_firmware, "unlock-firmware-code", - "Remove .pcrlock file for firmware code"); -static int verb_unlock_firmware(int argc, char *argv[], uintptr_t _data, void *userdata) { +VERB(verb_lock_firmware, "lock-firmware-code", NULL, VERB_ANY, 2, 0, + "Generate a .pcrlock file from current firmware code"); +static int verb_lock_firmware(int argc, char *argv[], uintptr_t _data, void *userdata) { + return lock_firmware(endswith(argv[0], "firmware-code")); +} + +static int unlock_firmware(bool is_code) { const char *default_pcrlock_early_path, *default_pcrlock_late_path; int r; - if (endswith(argv[0], "firmware-code")) { + if (is_code) { default_pcrlock_early_path = PCRLOCK_FIRMWARE_CODE_EARLY_PATH; default_pcrlock_late_path = PCRLOCK_FIRMWARE_CODE_LATE_PATH; } else { @@ -4327,15 +4328,19 @@ static int verb_unlock_firmware(int argc, char *argv[], uintptr_t _data, void *u return 0; } +VERB_NOARG(verb_unlock_firmware, "unlock-firmware-code", + "Remove .pcrlock file for firmware code"); +static int verb_unlock_firmware(int argc, char *argv[], uintptr_t _data, void *userdata) { + return unlock_firmware(endswith(argv[0], "firmware-code")); +} + VERB(verb_lock_firmware, "lock-firmware-config", NULL, VERB_ANY, 2, 0, "Generate a .pcrlock file from current firmware configuration"); VERB_NOARG(verb_unlock_firmware, "unlock-firmware-config", "Remove .pcrlock file for firmware configuration"); -VERB_NOARG(verb_lock_secureboot_policy, "lock-secureboot-policy", - "Generate a .pcrlock file from current SecureBoot policy"); -static int verb_lock_secureboot_policy(int argc, char *argv[], uintptr_t _data, void *userdata) { +static int lock_secureboot_policy(void) { static const struct { sd_id128_t id; const char *name; @@ -4408,6 +4413,12 @@ static int verb_lock_secureboot_policy(int argc, char *argv[], uintptr_t _data, return write_pcrlock(array, PCRLOCK_SECUREBOOT_POLICY_PATH); } +VERB_NOARG(verb_lock_secureboot_policy, "lock-secureboot-policy", + "Generate a .pcrlock file from current SecureBoot policy"); +static int verb_lock_secureboot_policy(int argc, char *argv[], uintptr_t _data, void *userdata) { + return lock_secureboot_policy(); +} + VERB_NOARG(verb_unlock_secureboot_policy, "unlock-secureboot-policy", "Remove .pcrlock file for SecureBoot policy"); static int verb_unlock_secureboot_policy(int argc, char *argv[], uintptr_t _data, void *userdata) { @@ -4531,9 +4542,7 @@ static int event_log_ensure_secureboot_consistency(EventLog *el) { return 0; } -VERB_NOARG(verb_lock_secureboot_authority, "lock-secureboot-authority", - "Generate a .pcrlock file from current SecureBoot authority"); -static int verb_lock_secureboot_authority(int argc, char *argv[], uintptr_t _data, void *userdata) { +static int lock_secureboot_authority(void) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL; _cleanup_(event_log_freep) EventLog *el = NULL; int r; @@ -4612,6 +4621,12 @@ static int verb_lock_secureboot_authority(int argc, char *argv[], uintptr_t _dat return write_pcrlock(array, PCRLOCK_SECUREBOOT_AUTHORITY_PATH); } +VERB_NOARG(verb_lock_secureboot_authority, "lock-secureboot-authority", + "Generate a .pcrlock file from current SecureBoot authority"); +static int verb_lock_secureboot_authority(int argc, char *argv[], uintptr_t _data, void *userdata) { + return lock_secureboot_authority(); +} + VERB_NOARG(verb_unlock_secureboot_authority, "unlock-secureboot-authority", "Remove .pcrlock file for SecureBoot authority"); static int verb_unlock_secureboot_authority(int argc, char *argv[], uintptr_t _data, void *userdata) { @@ -5470,6 +5485,75 @@ static int vl_method_remove_policy(sd_varlink *link, sd_json_variant *parameters return sd_varlink_reply(link, NULL); } +typedef enum LockCategory { + LOCK_CATEGORY_FIRMWARE_CODE, + LOCK_CATEGORY_FIRMWARE_CONFIG, + LOCK_CATEGORY_SECUREBOOT_POLICY, + LOCK_CATEGORY_SECUREBOOT_AUTHORITY, + _LOCK_CATEGORY_MAX, + _LOCK_CATEGORY_INVALID = -EINVAL, +} LockCategory; + +static const char* const lock_category_table[_LOCK_CATEGORY_MAX] = { + [LOCK_CATEGORY_FIRMWARE_CODE] = "firmwareCode", + [LOCK_CATEGORY_FIRMWARE_CONFIG] = "firmwareConfig", + [LOCK_CATEGORY_SECUREBOOT_POLICY] = "secureBootPolicy", + [LOCK_CATEGORY_SECUREBOOT_AUTHORITY] = "secureBootAuthority", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(lock_category, LockCategory); +static JSON_DISPATCH_ENUM_DEFINE(dispatch_lock_category, LockCategory, lock_category_from_string); + +typedef struct MethodLockParameters { + LockCategory category; + bool lock; +} MethodLockParameters; + +static int vl_method_lock(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + static const sd_json_dispatch_field dispatch_table[] = { + { "category", SD_JSON_VARIANT_STRING, dispatch_lock_category, offsetof(MethodLockParameters, category), SD_JSON_MANDATORY }, + { "lock", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(MethodLockParameters, lock), 0 }, + {} + }; + MethodLockParameters p = { + .category = _LOCK_CATEGORY_INVALID, + .lock = true, + }; + int r; + + assert(link); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + switch (p.category) { + + case LOCK_CATEGORY_FIRMWARE_CODE: + r = p.lock ? lock_firmware(/* is_code= */ true) : unlock_firmware(/* is_code= */ true); + break; + + case LOCK_CATEGORY_FIRMWARE_CONFIG: + r = p.lock ? lock_firmware(/* is_code= */ false) : unlock_firmware(/* is_code= */ false); + break; + + case LOCK_CATEGORY_SECUREBOOT_POLICY: + r = p.lock ? lock_secureboot_policy() : unlink_pcrlock(PCRLOCK_SECUREBOOT_POLICY_PATH); + break; + + case LOCK_CATEGORY_SECUREBOOT_AUTHORITY: + r = p.lock ? lock_secureboot_authority() : unlink_pcrlock(PCRLOCK_SECUREBOOT_AUTHORITY_PATH); + break; + + default: + assert_not_reached(); + } + if (r < 0) + return r; + + return sd_varlink_reply(link, NULL); +} + static int vl_method_on_completed_update(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { int r; @@ -5532,6 +5616,7 @@ static int run(int argc, char *argv[]) { "io.systemd.PCRLock.ReadEventLog", vl_method_read_event_log, "io.systemd.PCRLock.MakePolicy", vl_method_make_policy, "io.systemd.PCRLock.RemovePolicy", vl_method_remove_policy, + "io.systemd.PCRLock.Lock", vl_method_lock, "io.systemd.SysUpdate.Notify.OnCompletedUpdate", vl_method_on_completed_update); if (r < 0) return log_error_errno(r, "Failed to bind Varlink methods: %m"); diff --git a/src/shared/varlink-io.systemd.PCRLock.c b/src/shared/varlink-io.systemd.PCRLock.c index 15e37cd2845..c1e8876b1d4 100644 --- a/src/shared/varlink-io.systemd.PCRLock.c +++ b/src/shared/varlink-io.systemd.PCRLock.c @@ -14,6 +14,24 @@ static SD_VARLINK_DEFINE_METHOD( static SD_VARLINK_DEFINE_METHOD( RemovePolicy); +static SD_VARLINK_DEFINE_ENUM_TYPE( + LockCategory, + SD_VARLINK_FIELD_COMMENT("Firmware code measurements (PCRs 0, 2, 4)."), + SD_VARLINK_DEFINE_ENUM_VALUE(firmwareCode), + SD_VARLINK_FIELD_COMMENT("Firmware configuration measurements (PCRs 1, 3, 5)."), + SD_VARLINK_DEFINE_ENUM_VALUE(firmwareConfig), + SD_VARLINK_FIELD_COMMENT("SecureBoot policy, i.e. the SecureBoot, PK, KEK, db and dbx EFI variables (PCR 7)."), + SD_VARLINK_DEFINE_ENUM_VALUE(secureBootPolicy), + SD_VARLINK_FIELD_COMMENT("SecureBoot authority measurements, i.e. the certificates used to validate the boot components (PCR 7)."), + SD_VARLINK_DEFINE_ENUM_VALUE(secureBootAuthority)); + +static SD_VARLINK_DEFINE_METHOD( + Lock, + SD_VARLINK_FIELD_COMMENT("The category of measurements to generate or remove a .pcrlock file for."), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(category, LockCategory, 0), + SD_VARLINK_FIELD_COMMENT("If true (the default), generate the .pcrlock file(s) for the selected category; if false, remove them again."), + SD_VARLINK_DEFINE_INPUT(lock, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE)); + static SD_VARLINK_DEFINE_ERROR( NoChange); @@ -23,4 +41,8 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_ReadEventLog, &vl_method_MakePolicy, &vl_method_RemovePolicy, + SD_VARLINK_SYMBOL_COMMENT("The category of measurements a .pcrlock file can be generated for or removed, as used by the Lock() method."), + &vl_type_LockCategory, + SD_VARLINK_SYMBOL_COMMENT("Generates or removes the .pcrlock file(s) for the selected category of measurements. Generates (locks) them by default, or removes (unlocks) them if 'lock' is false."), + &vl_method_Lock, &vl_error_NoChange); diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh index 8701d60119c..1b6a063adf2 100755 --- a/test/units/TEST-70-TPM2.pcrlock.sh +++ b/test/units/TEST-70-TPM2.pcrlock.sh @@ -242,6 +242,11 @@ systemctl restart systemd-pcrlock.socket varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}' varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}' varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}' +varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.Lock '{"category":"firmwareConfig"}' +varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.Lock '{"category":"firmwareConfig","lock":false}' +varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.Lock '{"category":"firmwareCode","lock":false}' +varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.Lock '{"category":"secureBootPolicy","lock":false}' +varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.Lock '{"category":"secureBootAuthority","lock":false}' rm "$img" /tmp/pcrlockpwd