]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrextend: add secret parameter to varlink interface
authorPaul Meyer <katexochen0@gmail.com>
Tue, 21 Jul 2026 13:40:18 +0000 (15:40 +0200)
committerPaul Meyer <katexochen0@gmail.com>
Tue, 28 Jul 2026 06:58:25 +0000 (08:58 +0200)
Add an optional 'secret' input to io.systemd.PCRExtend.Extend. When set,
the HMAC of the measured data keyed by the secret is extended instead of
a plain hash, matching the existing tpm2_{pcr,nvpcr}_extend_bytes()
secret parameter. This lets callers measure a secret (e.g. a volume key)
without leaking a hash of it.

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
src/pcrextend/pcrextend.c
src/shared/varlink-io.systemd.PCRExtend.c

index 132f20b101bd8f8e4143dd6bd28cc5ac522e188c..06cc7dc5a73837a799f93b72abe181d3210e0529 100644 (file)
@@ -302,6 +302,7 @@ static int tpm2_context_new_for_measurement(Tpm2Context **ret) {
 static int extend_pcr_now(
                 uint32_t pcr_mask,
                 const struct iovec *data,
+                const struct iovec *secret,
                 Tpm2UserspaceEventType event) {
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
@@ -331,7 +332,7 @@ static int extend_pcr_now(
         BIT_FOREACH(pcr, pcr_mask) {
                 log_debug("Measuring '%s' into PCR index %i, banks %s.", safe, pcr, joined_banks);
 
-                r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, data, /* secret= */ NULL, event, safe);
+                r = tpm2_pcr_extend_bytes(c, arg_banks, pcr, data, secret, event, safe);
                 if (r < 0)
                         return log_error_errno(r, "Could not extend PCR: %m");
 
@@ -349,6 +350,7 @@ static int extend_pcr_now(
 static int extend_nvpcr_now(
                 const char *name,
                 const struct iovec *data,
+                const struct iovec *secret,
                 Tpm2UserspaceEventType event) {
 
         _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
@@ -371,7 +373,7 @@ static int extend_nvpcr_now(
                         /* session= */ NULL,
                         name,
                         data,
-                        /* secret= */ NULL,
+                        secret,
                         event,
                         safe);
         if (r == -ENOBUFS)
@@ -393,6 +395,7 @@ typedef struct MethodExtendParameters {
         const char *nvpcr;
         const char *text;
         struct iovec data;
+        struct iovec secret;
         Tpm2UserspaceEventType event_type;
 } MethodExtendParameters;
 
@@ -400,6 +403,7 @@ static void method_extend_parameters_done(MethodExtendParameters *p) {
         assert(p);
 
         iovec_done(&p->data);
+        iovec_done_erase(&p->secret);
 }
 
 static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_tpm2_userspace_event_type, Tpm2UserspaceEventType, tpm2_userspace_event_type_from_string);
@@ -407,10 +411,11 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_tpm2_userspace_event_type, Tpm2Us
 static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
 
         static const sd_json_dispatch_field dispatch_table[] = {
-                { "pcr",       _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,                   offsetof(MethodExtendParameters, pcr),   0 },
-                { "nvpcr",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,           offsetof(MethodExtendParameters, nvpcr), 0 },
-                { "text",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,           offsetof(MethodExtendParameters, text),  0 },
-                { "data",      SD_JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec,            offsetof(MethodExtendParameters, data),   0 },
+                { "pcr",       _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,                   offsetof(MethodExtendParameters, pcr),        0 },
+                { "nvpcr",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,           offsetof(MethodExtendParameters, nvpcr),      0 },
+                { "text",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,           offsetof(MethodExtendParameters, text),       0 },
+                { "data",      SD_JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec,            offsetof(MethodExtendParameters, data),       0 },
+                { "secret",    SD_JSON_VARIANT_STRING,        json_dispatch_unbase64_iovec,            offsetof(MethodExtendParameters, secret),     0 },
                 { "eventType", SD_JSON_VARIANT_STRING,        json_dispatch_tpm2_userspace_event_type, offsetof(MethodExtendParameters, event_type), 0 },
                 {}
         };
@@ -456,13 +461,13 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
                 return sd_varlink_error_invalid_parameter_name(link, p.text ? "text" : "data");
 
         if (p.nvpcr) {
-                r = extend_nvpcr_now(p.nvpcr, extend_iovec, p.event_type);
+                r = extend_nvpcr_now(p.nvpcr, extend_iovec, &p.secret, p.event_type);
                 if (IN_SET(r, -ENOENT, -ENODEV))
                         return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL);
                 if (r == -ENOBUFS)
                         return sd_varlink_error(link, "io.systemd.PCRExtend.NvPCRSpaceExhausted", NULL);
         } else
-                r = extend_pcr_now(INDEX_TO_MASK(uint32_t, p.pcr), extend_iovec, p.event_type);
+                r = extend_pcr_now(INDEX_TO_MASK(uint32_t, p.pcr), extend_iovec, &p.secret, p.event_type);
         if (r < 0)
                 return r;
 
@@ -474,7 +479,7 @@ static int vl_server(void) {
         int r;
 
         r = varlink_server_new(&varlink_server,
-                               SD_VARLINK_SERVER_ROOT_ONLY | SD_VARLINK_SERVER_MYSELF_ONLY,
+                               SD_VARLINK_SERVER_ROOT_ONLY | SD_VARLINK_SERVER_MYSELF_ONLY | SD_VARLINK_SERVER_INPUT_SENSITIVE,
                                /* userdata= */ NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate Varlink server: %m");
@@ -593,9 +598,9 @@ static int run(int argc, char *argv[]) {
         }
 
         if (arg_nvpcr_name)
-                r = extend_nvpcr_now(arg_nvpcr_name, &IOVEC_MAKE(word, strlen(word)), event);
+                r = extend_nvpcr_now(arg_nvpcr_name, &IOVEC_MAKE(word, strlen(word)), NULL, event);
         else
-                r = extend_pcr_now(arg_pcr_mask, &IOVEC_MAKE(word, strlen(word)), event);
+                r = extend_pcr_now(arg_pcr_mask, &IOVEC_MAKE(word, strlen(word)), NULL, event);
         /* Both extend paths report "TPM cannot be used for this measurement" (no PCR bank, missing crypto,
          * no TPM device — see tpm2_context_new_for_measurement()) as -EOPNOTSUPP. Under --graceful we skip
          * those rather than fail and block boot. Genuine faults keep their own errno and are never
index cf42bf9624697134afa0d377c527d0ddd61e4adf..62deaf60618b0e7dc42a24f232e0f9a942e6574a 100644 (file)
@@ -25,6 +25,8 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(text, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Binary data to measure, encoded in Base64. (Specify either this, or the 'text' field above, not both)"),
                 SD_VARLINK_DEFINE_INPUT(data, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("HMAC key, encoded in Base64. When set, the HMAC of the measured data keyed by this secret is extended rather than a plain hash."),
+                SD_VARLINK_DEFINE_INPUT(secret, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("Event type to include in the (userspace) event log). This is optional, and mostly for debugging."),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(eventType, EventType, SD_VARLINK_NULLABLE));