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 <dongshengyuan@uniontech.com>
This commit is contained in:
dongshengyuan
2026-07-09 11:02:46 +02:00
committed by Lennart Poettering
parent 4839cb914e
commit 8cbae52fc9
2 changed files with 82 additions and 17 deletions
+71 -13
View File
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#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");
}
+11 -4
View File
@@ -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