From c4ccb80e392856156a4876b06cecf37f7763d20b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 13 Jun 2023 16:48:20 +0200 Subject: [PATCH] bootctl: warn if the ESP random seed is stored on a world-readable dir This takes heavy inspiration from @zx2c4 (Jason A. Donenfeld)'s PR #25531 but changes it considerably, but always going by fd instead of paths, and only warning about the side file itself and the ESP mount point, nothing else. This shuld be more than enough and should not be brittle against concurrent path modifications. Replaces: #25531 --- TODO | 2 -- src/boot/bootctl-random-seed.c | 43 +++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index d47d860a571..5e3cac95485 100644 --- a/TODO +++ b/TODO @@ -390,8 +390,6 @@ Features: Usecase: provide a minimal ESP with sd-boot and a couple of these sd-fetch binaries in place of UKIs, and download them on-the-fly. -* bootctl: warn if ESP is mounted world-readable (and in particular the seed). - * maybe: systemd-loop-generator that sets up loopback devices if requested via kernel cmdline. usecase: include encrypted/verity root fs in UKI. diff --git a/src/boot/bootctl-random-seed.c b/src/boot/bootctl-random-seed.c index deda4debd20..95ff73cc875 100644 --- a/src/boot/bootctl-random-seed.c +++ b/src/boot/bootctl-random-seed.c @@ -9,6 +9,7 @@ #include "fd-util.h" #include "find-esp.h" #include "fs-util.h" +#include "glyph-util.h" #include "io-util.h" #include "mkdir.h" #include "path-util.h" @@ -17,6 +18,39 @@ #include "tmpfile-util.h" #include "umask-util.h" +static int random_seed_verify_permissions(int fd, mode_t expected_type) { + _cleanup_free_ char *full_path = NULL; + struct stat st; + int r; + + assert(fd >= 0); + + r = fd_get_path(fd, &full_path); + if (r < 0) + return log_error_errno(r, "Unable to determine full path of random seed fd: %m"); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Unable to stat %s: %m", full_path); + + if (((st.st_mode ^ expected_type) & S_IFMT) != 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADF), + "Unexpected inode type when validating random seed access mode on %s: %m", full_path); + + if ((st.st_mode & 0007) == 0) /* All world bits are off? Then all is good */ + return 0; + + if (S_ISREG(expected_type)) + log_warning("%s Random seed file '%s' is world accessible, which is a security hole! %s", + special_glyph(SPECIAL_GLYPH_WARNING_SIGN), full_path, special_glyph(SPECIAL_GLYPH_WARNING_SIGN)); + else { + assert(S_ISDIR(expected_type)); + log_warning("%s Mount point '%s' which backs the random seed file is world accessible, which is a security hole! %s", + special_glyph(SPECIAL_GLYPH_WARNING_SIGN), full_path, special_glyph(SPECIAL_GLYPH_WARNING_SIGN)); + } + + return 1; +} + static int set_system_token(void) { uint8_t buffer[RANDOM_EFI_SEED_SIZE]; size_t token_size; @@ -90,7 +124,7 @@ int install_random_seed(const char *esp) { _cleanup_free_ char *tmp = NULL; uint8_t buffer[RANDOM_EFI_SEED_SIZE]; struct sha256_ctx hash_state; - bool refreshed; + bool refreshed, warned = false; int r; assert(esp); @@ -101,6 +135,8 @@ int install_random_seed(const char *esp) { if (esp_fd < 0) return log_error_errno(errno, "Failed to open ESP directory '%s': %m", esp); + (void) random_seed_verify_permissions(esp_fd, S_IFDIR); + loader_dir_fd = open_mkdir_at(esp_fd, "loader", O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOFOLLOW, 0775); if (loader_dir_fd < 0) return log_error_errno(loader_dir_fd, "Failed to open loader directory '%s/loader': %m", esp); @@ -122,6 +158,8 @@ int install_random_seed(const char *esp) { } else { ssize_t n; + warned = random_seed_verify_permissions(fd, S_IFREG) > 0; + /* Hash the old seed in so that we never regress in entropy. */ n = read(fd, buffer, sizeof(buffer)); @@ -143,6 +181,9 @@ int install_random_seed(const char *esp) { if (fd < 0) return log_error_errno(fd, "Failed to open random seed file for writing: %m"); + if (!warned) /* only warn once per seed file */ + (void) random_seed_verify_permissions(fd, S_IFREG); + r = loop_write(fd, buffer, sizeof(buffer), /* do_poll= */ false); if (r < 0) { log_error_errno(r, "Failed to write random seed file: %m");