]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrlock: handle piped PE input
authordongshengyuan <545258830@qq.com>
Mon, 6 Jul 2026 04:55:40 +0000 (12:55 +0800)
committerLennart Poettering <lennart@poettering.net>
Thu, 9 Jul 2026 09:02:46 +0000 (11:02 +0200)
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>
src/pcrlock/pcrlock.c
test/units/TEST-70-TPM2.pcrlock.sh

index 1b4d90d2a831665c8241127b16616b8e4d186f8c..a1bc5c2bea351a72b9cdae7b3efc7b04129a0b75 100644 (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");
         }
index a9680af8df0b2cfcb67592987648062f3c1f9b02..ae809be6712fd9dd8451b7af5c2328cea8f1f6a3 100755 (executable)
@@ -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