]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: warn if the ESP random seed is stored on a world-readable dir
authorLennart Poettering <lennart@poettering.net>
Tue, 13 Jun 2023 14:48:20 +0000 (16:48 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 14 Jun 2023 16:00:24 +0000 (18:00 +0200)
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
src/boot/bootctl-random-seed.c

diff --git a/TODO b/TODO
index d47d860a57123564784450fa2037ff756ba22554..5e3cac95485d6b9514bc07fdfab4e947f18b773b 100644 (file)
--- 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.
 
index deda4debd2066692b7469b1a4af1912390555cca..95ff73cc875bdb840d2005c7bbc2c8eb92cd3b6b 100644 (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"
 #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");