pcrextend: skip measurement gracefully when the TPM can't be used
So far --graceful only short-circuited when no TPM was present at all (the !tpm2_is_mostly_supported() check). If a TPM is present but can't actually be used for the measurement we want, the measurement still failed hard. For systemd-pcrextend instances ordered before sysinit.target this pushes the system to degraded and blocks boot, which defeats the purpose of --graceful. Make the two extend helpers report every "TPM is present-or-absent but cannot be used for this measurement" condition with a single errno, -EOPNOTSUPP: no usable PCR bank, no TPM device, missing crypto (e.g. AES-128-CFB), no NvPCR support, and OpenSSL-less builds. tpm2_context_new_or_warn() reports a missing device as -ENOENT, so each helper translates that to -EOPNOTSUPP at the call site, keeping every errno single-meaning. Co-developed-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Zbigniew Jędrzejewski-Szmek
parent
f61e1e5cf4
commit
c925e405f3
@@ -261,6 +261,48 @@ static int escape_and_truncate_data(const void *data, size_t size, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm2_context_new_for_measurement(Tpm2Context **ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Wrapper around tpm2_context_new_or_warn() that translates a missing TPM device from -ENOENT to
|
||||
* -EOPNOTSUPP, for two reasons:
|
||||
*
|
||||
* - It disambiguates -ENOENT on the NvPCR path. There -ENOENT also means "no such NvPCR definition"
|
||||
* (from tpm2_nvpcr_extend_bytes() → nvpcr_data_load()), which vl_method_extend() maps to the
|
||||
* io.systemd.PCRExtend.NoSuchNvPCR error. If "no TPM device" stayed -ENOENT it would be
|
||||
* misreported as a missing NvPCR definition. So we keep each errno single-meaning: -ENOENT = "no
|
||||
* such NvPCR definition", -EOPNOTSUPP = "TPM cannot be used for this measurement" (also what
|
||||
* tpm2_context_new_or_warn() already returns for missing crypto / an OpenSSL-less build, and what
|
||||
* extend_pcr_now() returns when no PCR bank is enabled).
|
||||
*
|
||||
* - It lets the --graceful skip in run() match a single errno (-EOPNOTSUPP).
|
||||
*
|
||||
* We deliberately translate *only* -ENOENT here, not every "TPM unusable" errno:
|
||||
*
|
||||
* - Genuine absence (no TPM hardware, no tpm2 libraries) never reaches this point under --graceful:
|
||||
* run() bails out earlier at the tpm2_is_mostly_supported() guard, which requires the kernel
|
||||
* driver + tpm subsystem + libtss2 esys/rc/mu. So broadening the set buys nothing for the common
|
||||
* "no TPM" case.
|
||||
*
|
||||
* - -ENOPKG (TCTI driver libtss2-tcti-device.so.0 not loadable) can therefore only happen when the
|
||||
* rest of the stack *is* present — i.e. a half-installed tpm2-tss, a misconfiguration.
|
||||
*
|
||||
* - -ENOTRECOVERABLE (TCTI/Esys init or TPM startup failed) means a TPM is present but
|
||||
* malfunctioning.
|
||||
*
|
||||
* Both of the latter are real faults we want to surface and fail on, not silently skip:
|
||||
* --graceful's contract is "no TPM2 device is found", i.e. absence, not breakage. -EINVAL (bad
|
||||
* device string), -ENOMEM, etc. likewise stay hard errors. */
|
||||
|
||||
r = tpm2_context_new_or_warn(arg_tpm2_device, ret);
|
||||
if (r == -ENOENT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int extend_pcr_now(
|
||||
uint32_t pcr_mask,
|
||||
const void *data,
|
||||
@@ -272,7 +314,7 @@ static int extend_pcr_now(
|
||||
|
||||
assert(pcr_mask != 0);
|
||||
|
||||
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
|
||||
r = tpm2_context_new_for_measurement(&c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -280,7 +322,7 @@ static int extend_pcr_now(
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (strv_isempty(arg_banks)) /* Still none? */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Found a TPM2 without enabled PCR banks. Can't operate.");
|
||||
|
||||
_cleanup_free_ char *joined_banks = NULL;
|
||||
joined_banks = strv_join(arg_banks, ", ");
|
||||
@@ -320,7 +362,7 @@ static int extend_nvpcr_now(
|
||||
|
||||
assert(name);
|
||||
|
||||
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
|
||||
r = tpm2_context_new_for_measurement(&c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -556,6 +598,14 @@ static int run(int argc, char *argv[]) {
|
||||
r = extend_nvpcr_now(arg_nvpcr_name, word, strlen(word), event);
|
||||
else
|
||||
r = extend_pcr_now(arg_pcr_mask, word, strlen(word), 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
|
||||
* suppressed. */
|
||||
if (arg_graceful && r == -EOPNOTSUPP) {
|
||||
log_notice_errno(r, "TPM2 cannot be used for measurement (no usable PCR bank, missing device, or missing crypto support), skipping gracefully.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user