From: Paul Meyer Date: Tue, 21 Jul 2026 13:40:18 +0000 (+0200) Subject: pcrextend: add secret parameter to varlink interface X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=16c75b8d3b499089cbc3f66a0c0fe7ea465dc442;p=thirdparty%2Fsystemd.git pcrextend: add secret parameter to varlink interface 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 --- diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index 132f20b101b..06cc7dc5a73 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -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 diff --git a/src/shared/varlink-io.systemd.PCRExtend.c b/src/shared/varlink-io.systemd.PCRExtend.c index cf42bf96246..62deaf60618 100644 --- a/src/shared/varlink-io.systemd.PCRExtend.c +++ b/src/shared/varlink-io.systemd.PCRExtend.c @@ -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));