From: dongshengyuan <545258830@qq.com> Date: Mon, 6 Jul 2026 04:55:40 +0000 (+0800) Subject: pcrlock: handle piped PE input X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8cbae52fc9dca372d103b1ff117fed9b0aa53071;p=thirdparty%2Fsystemd.git pcrlock: handle piped PE input When lock-pe or lock-uki read from stdin, copy non-regular input to a seekable temporary fd before hashing it. Fixes #42893. Signed-off-by: dongshengyuan --- diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c index 1b4d90d2a83..a1bc5c2bea3 100644 --- a/src/pcrlock/pcrlock.c +++ b/src/pcrlock/pcrlock.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include #include #include "sd-device.h" @@ -17,6 +18,7 @@ #include "chase.h" #include "color-util.h" #include "conf-files.h" +#include "copy.h" #include "creds-util.h" #include "crypto-util.h" #include "efi-api.h" @@ -55,6 +57,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "tmpfile-util.h" #include "tpm2-pcr.h" #include "tpm2-util.h" #include "unaligned.h" @@ -110,6 +113,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep); #define PCRLOCK_MACHINE_ID_PATH "/var/lib/pcrlock.d/820-machine-id.pcrlock" #define PCRLOCK_ROOT_FILE_SYSTEM_PATH "/var/lib/pcrlock.d/830-root-file-system.pcrlock" #define PCRLOCK_FILE_SYSTEM_PATH_PREFIX "/var/lib/pcrlock.d/840-file-system-" +#define PCRLOCK_PE_INPUT_MAX (2U * U64_GB) /* The default set of PCRs to lock to */ #define DEFAULT_PCR_MASK \ @@ -4768,6 +4772,64 @@ static int verb_unlock_gpt(int argc, char *argv[], uintptr_t _data, void *userda return unlink_pcrlock(PCRLOCK_GPT_PATH); } +static int pe_stdin_too_large(void) { + return log_error_errno(SYNTHETIC_ERRNO(EFBIG), + "PE/UKI binary from stdin is larger than the maximum supported size of %s.", + FORMAT_BYTES(PCRLOCK_PE_INPUT_MAX)); +} + +static int acquire_stdin_pe_fd(void) { + _cleanup_close_ int fd = -EBADF; + const char *td; + struct stat st; + int r; + + if (fstat(STDIN_FILENO, &st) < 0) + return log_error_errno(errno, "Failed to stat stdin: %m"); + + if (S_ISREG(st.st_mode)) { + if ((uint64_t) st.st_size > PCRLOCK_PE_INPUT_MAX) + return pe_stdin_too_large(); + + /* Regular stdin is already seekable. Keep it directly to avoid copying large redirected + * PE/UKI files before hashing them. The size check above is a fast-path guard; a file that + * grows concurrently is still hashed stream-wise with bounded memory use. */ + fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 3); + if (fd < 0) + return log_error_errno(errno, "Failed to duplicate stdin: %m"); + + return TAKE_FD(fd); + } + + r = var_tmp_dir(&td); + if (r < 0) + return log_error_errno(r, "Failed to determine temporary directory: %m"); + + fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC); + if (fd < 0) + return log_error_errno(fd, "Failed to create temporary file for PE binary: %m"); + + r = copy_bytes(STDIN_FILENO, fd, PCRLOCK_PE_INPUT_MAX + 1, /* copy_flags= */ 0); + if (r < 0) + return log_error_errno(r, "Failed to copy PE binary from stdin to temporary file: %m"); + if (r > 0) + return pe_stdin_too_large(); + + return TAKE_FD(fd); +} + +static int acquire_pe_fd(const char *path) { + if (path) { + int fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open '%s': %m", path); + + return fd; + } + + return acquire_stdin_pe_fd(); +} + VERB(verb_lock_pe, "lock-pe", "[BINARY]", VERB_ANY, 2, 0, "Generate a .pcrlock file from PE binary"); static int verb_lock_pe(int argc, char *argv[], uintptr_t _data, void *userdata) { @@ -4778,11 +4840,9 @@ static int verb_lock_pe(int argc, char *argv[], uintptr_t _data, void *userdata) // FIXME: Maybe also generate a matching EV_EFI_VARIABLE_AUTHORITY records here for each signature that // covers this PE plus its hash, as alternatives under the same component name - if (argc >= 2) { - fd = open(argv[1], O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open '%s': %m", argv[1]); - } + fd = acquire_pe_fd(argc >= 2 ? argv[1] : NULL); + if (fd < 0) + return fd; if (arg_pcr_mask == 0) arg_pcr_mask = UINT32_C(1) << TPM2_PCR_BOOT_LOADER_CODE; @@ -4802,7 +4862,7 @@ static int verb_lock_pe(int argc, char *argv[], uintptr_t _data, void *userdata) assert_se(a = tpm2_hash_alg_to_string(*pa)); assert_se(md = sym_EVP_get_digestbyname(a)); - r = pe_hash(fd < 0 ? STDIN_FILENO : fd, md, &hash, &hash_size); + r = pe_hash(fd, md, &hash, &hash_size); if (r < 0) return log_error_errno(r, "Failed to hash PE binary: %m"); @@ -4852,11 +4912,9 @@ static int verb_lock_uki(int argc, char *argv[], uintptr_t _data, void *userdata if (arg_pcr_mask != 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PCR not configurable for UKI lock down."); - if (argc >= 2) { - fd = open(argv[1], O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open '%s': %m", argv[1]); - } + fd = acquire_pe_fd(argc >= 2 ? argv[1] : NULL); + if (fd < 0) + return fd; for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) { _cleanup_free_ void *peh = NULL; @@ -4866,7 +4924,7 @@ static int verb_lock_uki(int argc, char *argv[], uintptr_t _data, void *userdata assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i])); assert_se(md = sym_EVP_get_digestbyname(a)); - r = pe_hash(fd < 0 ? STDIN_FILENO : fd, md, &peh, hash_sizes + i); + r = pe_hash(fd, md, &peh, hash_sizes + i); if (r < 0) return log_error_errno(r, "Failed to hash PE binary: %m"); @@ -4877,7 +4935,7 @@ static int verb_lock_uki(int argc, char *argv[], uintptr_t _data, void *userdata if (r < 0) return log_error_errno(r, "Failed to build JSON digest object: %m"); - r = uki_hash(fd < 0 ? STDIN_FILENO : fd, md, section_hashes + (i * _UNIFIED_SECTION_MAX), hash_sizes + i); + r = uki_hash(fd, md, section_hashes + (i * _UNIFIED_SECTION_MAX), hash_sizes + i); if (r < 0) return log_error_errno(r, "Failed to UKI hash PE binary: %m"); } diff --git a/test/units/TEST-70-TPM2.pcrlock.sh b/test/units/TEST-70-TPM2.pcrlock.sh index a9680af8df0..ae809be6712 100755 --- a/test/units/TEST-70-TPM2.pcrlock.sh +++ b/test/units/TEST-70-TPM2.pcrlock.sh @@ -104,10 +104,17 @@ SYSTEMD_COLORS=256 "$SD_PCRLOCK" SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)" if [[ -n "$SD_STUB" ]]; then - "$SD_PCRLOCK" lock-pe "$SD_STUB" - "$SD_PCRLOCK" lock-pe <"$SD_STUB" - "$SD_PCRLOCK" lock-uki "$SD_STUB" - "$SD_PCRLOCK" lock-uki <"$SD_STUB" + pe_direct="$("$SD_PCRLOCK" lock-pe "$SD_STUB")" + pe_stdin="$("$SD_PCRLOCK" lock-pe <"$SD_STUB")" + pe_pipe="$(cat "$SD_STUB" | "$SD_PCRLOCK" lock-pe)" + [[ "$pe_direct" == "$pe_stdin" ]] + [[ "$pe_stdin" == "$pe_pipe" ]] + + uki_direct="$("$SD_PCRLOCK" lock-uki "$SD_STUB")" + uki_stdin="$("$SD_PCRLOCK" lock-uki <"$SD_STUB")" + uki_pipe="$(cat "$SD_STUB" | "$SD_PCRLOCK" lock-uki)" + [[ "$uki_direct" == "$uki_stdin" ]] + [[ "$uki_stdin" == "$uki_pipe" ]] fi PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query