From: Lennart Poettering Date: Fri, 19 Jul 2019 17:39:15 +0000 (+0200) Subject: core: take random seed from boot loader and credit it to kernel entropy pool X-Git-Tag: v243-rc1~32^2~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c18ecf0375021c5524bfe9d2f3d69180ffbb140d;p=thirdparty%2Fsystemd.git core: take random seed from boot loader and credit it to kernel entropy pool --- diff --git a/src/core/efi-random.c b/src/core/efi-random.c new file mode 100644 index 00000000000..c4d25d68e42 --- /dev/null +++ b/src/core/efi-random.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include +#include + +#include "alloc-util.h" +#include "chattr-util.h" +#include "efi-random.h" +#include "efivars.h" +#include "fd-util.h" +#include "fs-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) { + const char *p; + int r; + + /* 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(p, + "/sys/firmware/efi/efivars/LoaderRandomSeed-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", + "/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f") { + + r = chattr_path(p, 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", p); + + if (chmod(p, 0600) < 0) + log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", p); + } +} + +int efi_take_random_seed(void) { + _cleanup_free_ struct rand_pool_info *info = NULL; + _cleanup_free_ void *value = NULL; + _cleanup_close_ int random_fd = -1; + 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_VENDOR_LOADER, "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; + } + 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."); + + /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any chance for + * confusion here. */ + if (size > INT_MAX / 8) + size = INT_MAX / 8; + + random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); + if (random_fd < 0) + return log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m"); + + /* 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 laoder. */ + 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"); + + info = malloc(offsetof(struct rand_pool_info, buf) + size); + if (!info) + return log_oom(); + + info->entropy_count = size * 8; + info->buf_size = size; + memcpy(info->buf, value, size); + + if (ioctl(random_fd, RNDADDENTROPY, info) < 0) + return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m"); + + log_info("Successfully credited entropy passed from boot loader."); + return 1; +} diff --git a/src/core/efi-random.h b/src/core/efi-random.h new file mode 100644 index 00000000000..c1de8671d80 --- /dev/null +++ b/src/core/efi-random.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +int efi_take_random_seed(void); diff --git a/src/core/main.c b/src/core/main.c index ef17e71e119..0674e00ab01 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -32,9 +32,10 @@ #include "clock-util.h" #include "conf-parser.h" #include "cpu-set-util.h" -#include "dbus.h" #include "dbus-manager.h" +#include "dbus.h" #include "def.h" +#include "efi-random.h" #include "emergency-action.h" #include "env-util.h" #include "exit-status.h" @@ -2511,6 +2512,9 @@ int main(int argc, char *argv[]) { error_message = "Failed to mount API filesystems"; goto finish; } + + /* The efivarfs is now mounted, let's read the random seed off it */ + (void) efi_take_random_seed(); } /* Save the original RLIMIT_NOFILE/RLIMIT_MEMLOCK so that we can reset it later when diff --git a/src/core/meson.build b/src/core/meson.build index 34762dbc21a..267d65a3b22 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -66,6 +66,8 @@ libcore_sources = ''' device.h dynamic-user.c dynamic-user.h + efi-random.c + efi-random.h emergency-action.c emergency-action.h execute.c