summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2022-11-09 12:44:37 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2022-11-14 15:21:58 +0100
commit0be72218f1c90af5755ab40f94d047ee6864aea8 (patch)
tree16b446b19ceb21b9faf8471020a7ab1c35db2ea8 /src/core
parent87172c3df63c97ab2f680720b1141720ef66a985 (diff)
downloadsystemd-0be72218f1c90af5755ab40f94d047ee6864aea8.tar.gz
boot: implement kernel EFI RNG seed protocol with proper hashing
Rather than passing seeds up to userspace via EFI variables, pass seeds directly to the kernel's EFI stub loader, via LINUX_EFI_RANDOM_SEED_TABLE_GUID. EFI variables can potentially leak and suffer from forward secrecy issues, and processing these with userspace means that they are initialized much too late in boot to be useful. In contrast, LINUX_EFI_RANDOM_SEED_TABLE_GUID uses EFI configuration tables, and so is hidden from userspace entirely, and is parsed extremely early on by the kernel, so that every single call to get_random_bytes() by the kernel is seeded. In order to do this properly, we use a bit more robust hashing scheme, and make sure that each input is properly memzeroed out after use. The scheme is: key = HASH(LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN) new_disk_seed = HASH(key || 0) seed_for_linux = HASH(key || 1) The various inputs are: - LINUX_EFI_RANDOM_SEED_TABLE_GUID from prior bootloaders - 256 bits of seed from EFI's RNG - The (immutable) system token, from its EFI variable - The prior on-disk seed - The UEFI monotonic counter - A timestamp This also adjusts the secure boot semantics, so that the operation is only aborted if it's not possible to get random bytes from EFI's RNG or a prior boot stage. With the proper hashing scheme, this should make boot seeds safe even on secure boot. There is currently a bug in Linux's EFI stub in which if the EFI stub manages to generate random bytes on its own using EFI's RNG, it will ignore what the bootloader passes. That's annoying, but it means that either way, via systemd-boot or via EFI stub's mechanism, the RNG *does* get initialized in a good safe way. And this bug is now fixed in the efi.git tree, and will hopefully be backported to older kernels. As the kernel recommends, the resultant seeds are 256 bits and are allocated using pool memory of type EfiACPIReclaimMemory, so that it gets freed at the right moment in boot.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/efi-random.c82
-rw-r--r--src/core/efi-random.h2
-rw-r--r--src/core/main.c4
3 files changed, 16 insertions, 72 deletions
diff --git a/src/core/efi-random.c b/src/core/efi-random.c
index 4086b12739..61516775fc 100644
--- a/src/core/efi-random.c
+++ b/src/core/efi-random.c
@@ -12,79 +12,23 @@
#include "random-util.h"
#include "strv.h"
-/* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
- * the kernel's random pool, but only once per boot. If this is run very early during initialization we can
- * instantly boot up with a filled random pool.
- *
- * This makes no judgement on the entropy passed, it's the job of the boot loader to only pass us a seed that
- * is suitably validated. */
-
-static void lock_down_efi_variables(void) {
+void lock_down_efi_variables(void) {
+ _cleanup_close_ int fd = -1;
int r;
+ fd = open(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Unable to open LoaderSystemToken EFI variable, ignoring: %m");
+ return;
+ }
+
/* Paranoia: let's restrict access modes of these a bit, so that unprivileged users can't use them to
* identify the system or gain too much insight into what we might have credited to the entropy
* pool. */
- FOREACH_STRING(path,
- EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)),
- EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken))) {
-
- r = chattr_path(path, 0, FS_IMMUTABLE_FL, NULL);
- if (r == -ENOENT)
- continue;
- if (r < 0)
- log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", path);
-
- if (chmod(path, 0600) < 0)
- log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", path);
- }
-}
-
-int efi_take_random_seed(void) {
- _cleanup_free_ void *value = NULL;
- size_t size;
- int r;
-
- /* Paranoia comes first. */
- lock_down_efi_variables();
-
- if (access("/run/systemd/efi-random-seed-taken", F_OK) < 0) {
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to determine whether we already used the random seed token, not using it.");
- return 0;
- }
-
- /* ENOENT means we haven't used it yet. */
- } else {
- log_debug("EFI random seed already used, not using again.");
- return 0;
- }
-
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderRandomSeed), NULL, &value, &size);
- if (r == -EOPNOTSUPP) {
- log_debug_errno(r, "System lacks EFI support, not initializing random seed from EFI variable.");
- return 0;
- }
- if (r == -ENOENT) {
- log_debug_errno(r, "Boot loader did not pass LoaderRandomSeed EFI variable, not crediting any entropy.");
- return 0;
- }
+ r = chattr_fd(fd, 0, FS_IMMUTABLE_FL, NULL);
if (r < 0)
- return log_warning_errno(r, "Failed to read LoaderRandomSeed EFI variable, ignoring: %m");
-
- if (size == 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
-
- /* Before we use the seed, let's mark it as used, so that we never credit it twice. Also, it's a nice
- * way to let users known that we successfully acquired entropy from the boot loader. */
- r = touch("/run/systemd/efi-random-seed-taken");
- if (r < 0)
- return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
-
- r = random_write_entropy(-1, value, size, true);
- if (r < 0)
- return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
-
- log_info("Successfully credited entropy passed from boot loader.");
- return 1;
+ log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from LoaderSystemToken EFI variable, ignoring: %m");
+ if (fchmod(fd, 0600) < 0)
+ log_warning_errno(errno, "Failed to reduce access mode of LoaderSystemToken EFI variable, ignoring: %m");
}
diff --git a/src/core/efi-random.h b/src/core/efi-random.h
index 7d20fff57d..87166c9e3f 100644
--- a/src/core/efi-random.h
+++ b/src/core/efi-random.h
@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-int efi_take_random_seed(void);
+void lock_down_efi_variables(void);
diff --git a/src/core/main.c b/src/core/main.c
index cc725e6c42..119c518664 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2831,8 +2831,8 @@ int main(int argc, char *argv[]) {
goto finish;
}
- /* The efivarfs is now mounted, let's read the random seed off it */
- (void) efi_take_random_seed();
+ /* The efivarfs is now mounted, let's lock down the system token. */
+ lock_down_efi_variables();
/* Cache command-line options passed from EFI variables */
if (!skip_setup)