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.
This commit is contained in:
Luca Boccassi
2026-07-01 14:56:53 +01:00
parent 04f8927173
commit ef7b9f679b
3 changed files with 127 additions and 15 deletions
+100 -15
View File
@@ -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");
+22
View File
@@ -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);
+5
View File
@@ -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