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
This commit is contained in:
Lennart Poettering 2023-06-13 16:48:20 +02:00
parent 2b8628c704
commit c4ccb80e39
2 changed files with 42 additions and 3 deletions

2
TODO
View file

@ -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.

View file

@ -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");