]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrlock: add vl methods for fw and sb
authorLuca Boccassi <luca.boccassi@gmail.com>
Tue, 30 Jun 2026 23:47:22 +0000 (00:47 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 1 Jul 2026 13:56:53 +0000 (14:56 +0100)
Add varlink method wrappers for locking/unlocking firmware code and config,
and secure boot policy and authority.

src/pcrlock/pcrlock.c
src/shared/varlink-io.systemd.PCRLock.c
test/units/TEST-70-TPM2.pcrlock.sh

index 8b4ebceeb65b717513480f0be24b7fcac3b79655..60891c1e6601b6f1b4027e9d61ca07d52865a099 100644 (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");
index 15e37cd2845136bc62073ab08dcf9da15a369842..c1e8876b1d496b2d9e4f1e717b6d2c8cdf1ddf5a 100644 (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);
index 8701d60119cd001fc5fcba2f3747d24bac19b423..1b6a063adf2c565b8bee98581abe24cb297d2e7c 100755 (executable)
@@ -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