You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
9.3 KiB
214 lines
9.3 KiB
1 month ago
|
From 461ba7436d539258744d03400490ef100095e093 Mon Sep 17 00:00:00 2001
|
||
|
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
|
||
|
Date: Fri, 11 Nov 2022 15:22:35 +0100
|
||
|
Subject: [PATCH] random-seed: refresh EFI boot seed when writing a new seed
|
||
|
|
||
|
Since this runs at shutdown to write a new seed, we should also keep the
|
||
|
bootloader's seed maximally fresh by doing the same. So we follow the
|
||
|
same pattern - hash some new random bytes with the old seed to make a
|
||
|
new seed. We let this fail without warning, because it's just an
|
||
|
opportunistic thing. If the user happens to have set up the random seed
|
||
|
with bootctl, and the RNG is initialized, then things should be fine. If
|
||
|
not, we create a new seed if systemd-boot is in use. And if not, then we
|
||
|
just don't do anything.
|
||
|
|
||
|
(cherry picked from commit f913c784ad4c93894fd6cb2590738113dff5a694)
|
||
|
|
||
|
Related: RHEL-16952
|
||
|
---
|
||
|
src/random-seed/random-seed.c | 123 +++++++++++++++++++++++++++++++---
|
||
|
1 file changed, 112 insertions(+), 11 deletions(-)
|
||
|
|
||
|
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
|
||
|
index b548f92bbe..54ec3aa7d5 100644
|
||
|
--- a/src/random-seed/random-seed.c
|
||
|
+++ b/src/random-seed/random-seed.c
|
||
|
@@ -15,7 +15,11 @@
|
||
|
#include "sd-id128.h"
|
||
|
|
||
|
#include "alloc-util.h"
|
||
|
+#include "build.h"
|
||
|
+#include "chase-symlinks.h"
|
||
|
+#include "efi-loader.h"
|
||
|
#include "fd-util.h"
|
||
|
+#include "find-esp.h"
|
||
|
#include "fs-util.h"
|
||
|
#include "io-util.h"
|
||
|
#include "log.h"
|
||
|
@@ -25,6 +29,7 @@
|
||
|
#include "mkdir.h"
|
||
|
#include "parse-argument.h"
|
||
|
#include "parse-util.h"
|
||
|
+#include "path-util.h"
|
||
|
#include "pretty-print.h"
|
||
|
#include "random-util.h"
|
||
|
#include "string-table.h"
|
||
|
@@ -185,7 +190,7 @@ static int load_seed_file(
|
||
|
if (ret_hash_state) {
|
||
|
struct sha256_ctx *hash_state;
|
||
|
|
||
|
- hash_state = malloc(sizeof(struct sha256_ctx));
|
||
|
+ hash_state = new(struct sha256_ctx, 1);
|
||
|
if (!hash_state)
|
||
|
return log_oom();
|
||
|
|
||
|
@@ -311,6 +316,101 @@ static int save_seed_file(
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int refresh_boot_seed(void) {
|
||
|
+ uint8_t buffer[RANDOM_EFI_SEED_SIZE];
|
||
|
+ struct sha256_ctx hash_state;
|
||
|
+ _cleanup_free_ void *seed_file_bytes = NULL;
|
||
|
+ _cleanup_free_ char *esp_path = NULL;
|
||
|
+ _cleanup_close_ int seed_fd = -1;
|
||
|
+ size_t len;
|
||
|
+ ssize_t r;
|
||
|
+
|
||
|
+ assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
|
||
|
+
|
||
|
+ r = find_esp_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &esp_path,
|
||
|
+ NULL, NULL, NULL, NULL, NULL);
|
||
|
+ if (r < 0) {
|
||
|
+ if (r == -ENOKEY) {
|
||
|
+ log_debug_errno(r, "Couldn't find any ESP, so not updating ESP random seed.");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ return r; /* find_esp_and_warn() already logged */
|
||
|
+ }
|
||
|
+
|
||
|
+ seed_fd = chase_symlinks_and_open("/loader/random-seed", esp_path,
|
||
|
+ CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
|
||
|
+ O_RDWR|O_CLOEXEC|O_NOCTTY, NULL);
|
||
|
+ if (seed_fd == -ENOENT) {
|
||
|
+ uint64_t features;
|
||
|
+
|
||
|
+ r = efi_loader_get_features(&features);
|
||
|
+ if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
|
||
|
+ int dir_fd = chase_symlinks_and_open("/loader", esp_path,
|
||
|
+ CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
|
||
|
+ O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
|
||
|
+ if (dir_fd >= 0) {
|
||
|
+ seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
|
||
|
+ close(dir_fd);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (seed_fd < 0) {
|
||
|
+ log_debug_errno(seed_fd, "Failed to open EFI seed path: %m");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ r = random_seed_size(seed_fd, &len);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Failed to determine EFI seed path length: %m");
|
||
|
+ seed_file_bytes = malloc(len);
|
||
|
+ if (!seed_file_bytes)
|
||
|
+ return log_oom();
|
||
|
+ r = loop_read(seed_fd, seed_file_bytes, len, false);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Failed to read EFI seed file: %m");
|
||
|
+
|
||
|
+ /* Hash the old seed in so that we never regress in entropy. */
|
||
|
+ sha256_init_ctx(&hash_state);
|
||
|
+ sha256_process_bytes(&r, sizeof(r), &hash_state);
|
||
|
+ sha256_process_bytes(seed_file_bytes, r, &hash_state);
|
||
|
+
|
||
|
+ /* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
|
||
|
+ * RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
|
||
|
+ * than EFI seeding anyway, so there's no point in having non-getrandom() fallbacks here. So if this
|
||
|
+ * fails, just return early to cut our losses. */
|
||
|
+ r = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
|
||
|
+ if (r < 0) {
|
||
|
+ if (errno == EAGAIN) {
|
||
|
+ log_debug_errno(errno, "Random pool not initialized yet, so skipping EFI seed update");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ if (errno == ENOSYS) {
|
||
|
+ log_debug_errno(errno, "getrandom() not available, so skipping EFI seed update");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ return log_error_errno(errno, "Failed to generate random bytes for EFI seed: %m");
|
||
|
+ }
|
||
|
+ assert(r == sizeof(buffer));
|
||
|
+
|
||
|
+ /* Hash the new seed into the state containing the old one to generate our final seed. */
|
||
|
+ sha256_process_bytes(&r, sizeof(r), &hash_state);
|
||
|
+ sha256_process_bytes(buffer, r, &hash_state);
|
||
|
+ sha256_finish_ctx(&hash_state, buffer);
|
||
|
+
|
||
|
+ if (lseek(seed_fd, 0, SEEK_SET) < 0)
|
||
|
+ return log_error_errno(errno, "Failed to seek to beginning of EFI seed file: %m");
|
||
|
+ r = loop_write(seed_fd, buffer, sizeof(buffer), false);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(r, "Failed to write new EFI seed file: %m");
|
||
|
+ if (ftruncate(seed_fd, sizeof(buffer)) < 0)
|
||
|
+ return log_error_errno(errno, "Failed to truncate EFI seed file: %m");
|
||
|
+ r = fsync_full(seed_fd);
|
||
|
+ if (r < 0)
|
||
|
+ return log_error_errno(errno, "Failed to fsync EFI seed file: %m");
|
||
|
+
|
||
|
+ log_debug("Updated random seed in ESP");
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int help(int argc, char *argv[], void *userdata) {
|
||
|
_cleanup_free_ char *link = NULL;
|
||
|
int r;
|
||
|
@@ -402,15 +502,15 @@ static int run(int argc, char *argv[]) {
|
||
|
if (r < 0)
|
||
|
return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
|
||
|
|
||
|
+ random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||
|
+ if (random_fd < 0)
|
||
|
+ return log_error_errno(errno, "Failed to open /dev/urandom: %m");
|
||
|
+
|
||
|
/* When we load the seed we read it and write it to the device and then immediately update the saved
|
||
|
* seed with new data, to make sure the next boot gets seeded differently. */
|
||
|
|
||
|
switch (arg_action) {
|
||
|
case ACTION_LOAD:
|
||
|
- random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
|
||
|
- if (random_fd < 0)
|
||
|
- return log_error_errno(errno, "Failed to open /dev/urandom: %m");
|
||
|
-
|
||
|
/* First, let's write the machine ID into /dev/urandom, not crediting entropy. See
|
||
|
* load_machine_id() for an explanation why. */
|
||
|
load_machine_id(random_fd);
|
||
|
@@ -428,8 +528,10 @@ static int run(int argc, char *argv[]) {
|
||
|
|
||
|
log_full_errno(level, open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
|
||
|
log_full_errno(level, errno, "Failed to open " RANDOM_SEED " for reading: %m");
|
||
|
+ r = -errno;
|
||
|
|
||
|
- return missing ? 0 : -errno;
|
||
|
+ (void) refresh_boot_seed();
|
||
|
+ return missing ? 0 : r;
|
||
|
}
|
||
|
} else
|
||
|
write_seed_file = true;
|
||
|
@@ -439,10 +541,7 @@ static int run(int argc, char *argv[]) {
|
||
|
break;
|
||
|
|
||
|
case ACTION_SAVE:
|
||
|
- random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||
|
- if (random_fd < 0)
|
||
|
- return log_error_errno(errno, "Failed to open /dev/urandom: %m");
|
||
|
-
|
||
|
+ (void) refresh_boot_seed();
|
||
|
seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
|
||
|
if (seed_fd < 0)
|
||
|
return log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
|
||
|
@@ -460,9 +559,11 @@ static int run(int argc, char *argv[]) {
|
||
|
if (r < 0)
|
||
|
return r;
|
||
|
|
||
|
- if (read_seed_file)
|
||
|
+ if (read_seed_file) {
|
||
|
r = load_seed_file(seed_fd, random_fd, seed_size,
|
||
|
write_seed_file ? &hash_state : NULL);
|
||
|
+ (void) refresh_boot_seed();
|
||
|
+ }
|
||
|
|
||
|
if (r >= 0 && write_seed_file)
|
||
|
r = save_seed_file(seed_fd, random_fd, seed_size, synchronous, hash_state);
|