summaryrefslogtreecommitdiffstats
path: root/src/boot
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/boot
parentMerge pull request #25360 from poettering/strv-fixes (diff)
downloadsystemd-0be72218f1c90af5755ab40f94d047ee6864aea8.tar.xz
systemd-0be72218f1c90af5755ab40f94d047ee6864aea8.zip
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/boot')
-rw-r--r--src/boot/bootctl.c30
-rw-r--r--src/boot/efi/efi-string.h7
-rw-r--r--src/boot/efi/random-seed.c299
-rw-r--r--src/boot/efi/util.h25
4 files changed, 194 insertions, 167 deletions
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 0f142bff87..984b7e3a6d 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1886,8 +1886,6 @@ static int verb_status(int argc, char *argv[], void *userdata) {
printf("\n");
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
- have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
- printf(" Passed to OS: %s\n", yes_no(have));
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
printf(" System Token: %s\n", have ? "set" : "not set");
@@ -1977,10 +1975,10 @@ static int verb_list(int argc, char *argv[], void *userdata) {
static int install_random_seed(const char *esp) {
_cleanup_(unlink_and_freep) char *tmp = NULL;
- _cleanup_free_ void *buffer = NULL;
+ unsigned char buffer[RANDOM_EFI_SEED_SIZE];
_cleanup_free_ char *path = NULL;
_cleanup_close_ int fd = -1;
- size_t sz, token_size;
+ size_t token_size;
ssize_t n;
int r;
@@ -1990,13 +1988,7 @@ static int install_random_seed(const char *esp) {
if (!path)
return log_oom();
- sz = random_pool_size();
-
- buffer = malloc(sz);
- if (!buffer)
- return log_oom();
-
- r = crypto_random_bytes(buffer, sz);
+ r = crypto_random_bytes(buffer, sizeof(buffer));
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
@@ -2017,10 +2009,10 @@ static int install_random_seed(const char *esp) {
return log_error_errno(fd, "Failed to open random seed file for writing: %m");
}
- n = write(fd, buffer, sz);
+ n = write(fd, buffer, sizeof(buffer));
if (n < 0)
return log_error_errno(errno, "Failed to write random seed file: %m");
- if ((size_t) n != sz)
+ if ((size_t) n != sizeof(buffer))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
if (rename(tmp, path) < 0)
@@ -2028,7 +2020,7 @@ static int install_random_seed(const char *esp) {
tmp = mfree(tmp);
- log_info("Random seed file %s successfully written (%zu bytes).", path, sz);
+ log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
if (!arg_touch_variables)
return 0;
@@ -2080,16 +2072,16 @@ static int install_random_seed(const char *esp) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to test system token validity: %m");
} else {
- if (token_size >= sz) {
+ if (token_size >= sizeof(buffer)) {
/* Let's avoid writes if we can, and initialize this only once. */
log_debug("System token already written, not updating.");
return 0;
}
- log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sz);
+ log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
}
- r = crypto_random_bytes(buffer, sz);
+ r = crypto_random_bytes(buffer, sizeof(buffer));
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
@@ -2097,7 +2089,7 @@ static int install_random_seed(const char *esp) {
* and possibly get identification information or too much insight into the kernel's entropy pool
* state. */
RUN_WITH_UMASK(0077) {
- r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sz);
+ r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
if (r < 0) {
if (!arg_graceful)
return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
@@ -2107,7 +2099,7 @@ static int install_random_seed(const char *esp) {
else
log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
} else
- log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz);
+ log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
}
return 0;
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index 1ebd5fd6b7..d4d76a7c18 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -119,6 +119,13 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz
memcpy(dest, src, n);
return (uint8_t *) dest + n;
}
+
+static inline void explicit_bzero_safe(void *bytes, size_t len) {
+ if (!bytes || len == 0)
+ return;
+ memset(bytes, 0, len);
+ __asm__ __volatile__("": :"r"(bytes) :"memory");
+}
#else
/* For unit testing. */
int efi_memcmp(const void *p1, const void *p2, size_t n);
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index aea4f7e532..04bfd526f8 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -14,11 +14,24 @@
#define EFI_RNG_GUID &(const EFI_GUID) EFI_RNG_PROTOCOL_GUID
+struct linux_efi_random_seed {
+ uint32_t size;
+ uint8_t seed[];
+};
+
+#define LINUX_EFI_RANDOM_SEED_TABLE_GUID \
+ { 0x1ce1e5bc, 0x7ceb, 0x42f2, { 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b } }
+
/* SHA256 gives us 256/8=32 bytes */
#define HASH_VALUE_SIZE 32
-static EFI_STATUS acquire_rng(UINTN size, void **ret) {
- _cleanup_free_ void *data = NULL;
+/* Linux's RNG is 256 bits, so let's provide this much */
+#define DESIRED_SEED_SIZE 32
+
+/* Some basic domain separation in case somebody uses this data elsewhere */
+#define HASH_LABEL "systemd-boot random seed label v1"
+
+static EFI_STATUS acquire_rng(void *ret, UINTN size) {
EFI_RNG_PROTOCOL *rng;
EFI_STATUS err;
@@ -32,126 +45,9 @@ static EFI_STATUS acquire_rng(UINTN size, void **ret) {
if (!rng)
return EFI_UNSUPPORTED;
- data = xmalloc(size);
-
- err = rng->GetRNG(rng, NULL, size, data);
+ err = rng->GetRNG(rng, NULL, size, ret);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
-
- *ret = TAKE_PTR(data);
- return EFI_SUCCESS;
-}
-
-static void hash_once(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- UINTN counter,
- uint8_t ret[static HASH_VALUE_SIZE]) {
-
- /* This hashes together:
- *
- * 1. The contents of the old seed file
- * 2. Some random data acquired from the UEFI RNG (optional)
- * 3. Some 'system token' the installer installed as EFI variable (optional)
- * 4. The UEFI "monotonic counter" that increases with each boot
- * 5. A supplied counter value
- *
- * And writes the result to the specified buffer.
- */
-
- struct sha256_ctx hash;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
-
- sha256_init_ctx(&hash);
- sha256_process_bytes(old_seed, size, &hash);
- if (rng)
- sha256_process_bytes(rng, size, &hash);
- if (system_token_size > 0)
- sha256_process_bytes(system_token, system_token_size, &hash);
- sha256_process_bytes(&uefi_monotonic_counter, sizeof(uefi_monotonic_counter), &hash);
- sha256_process_bytes(&counter, sizeof(counter), &hash);
- sha256_finish_ctx(&hash, ret);
-}
-
-static EFI_STATUS hash_many(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- UINTN counter_start,
- UINTN n,
- void **ret) {
-
- _cleanup_free_ void *output = NULL;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
- assert(ret);
-
- /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
- * range counter_start…counter_start+n-1. */
-
- output = xmalloc_multiply(HASH_VALUE_SIZE, n);
-
- for (UINTN i = 0; i < n; i++)
- hash_once(old_seed, rng, size,
- system_token, system_token_size,
- uefi_monotonic_counter,
- counter_start + i,
- (uint8_t*) output + (i * HASH_VALUE_SIZE));
-
- *ret = TAKE_PTR(output);
- return EFI_SUCCESS;
-}
-
-static EFI_STATUS mangle_random_seed(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- void **ret_new_seed,
- void **ret_for_kernel) {
-
- _cleanup_free_ void *new_seed = NULL, *for_kernel = NULL;
- EFI_STATUS err;
- UINTN n;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
- assert(ret_new_seed);
- assert(ret_for_kernel);
-
- /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
- * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
- * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
- * kernel. To keep things simple, the new seed and kernel data have the same size as the old seed and
- * RNG data. */
-
- n = (size + HASH_VALUE_SIZE - 1) / HASH_VALUE_SIZE;
-
- /* Begin hashing in counter mode at counter 0 for the new seed for the disk */
- err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, 0, n, &new_seed);
- if (err != EFI_SUCCESS)
- return err;
-
- /* Continue counting at 'n' for the seed for the kernel */
- err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, n, n, &for_kernel);
- if (err != EFI_SUCCESS)
- return err;
-
- *ret_new_seed = TAKE_PTR(new_seed);
- *ret_for_kernel = TAKE_PTR(for_kernel);
-
return EFI_SUCCESS;
}
@@ -163,6 +59,7 @@ static EFI_STATUS acquire_system_token(void **ret, UINTN *ret_size) {
assert(ret);
assert(ret_size);
+ *ret_size = 0;
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND)
@@ -221,31 +118,83 @@ static void validate_sha256(void) {
}
EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
- _cleanup_free_ void *seed = NULL, *new_seed = NULL, *rng = NULL, *for_kernel = NULL, *system_token = NULL;
+ _cleanup_erase_ uint8_t random_bytes[DESIRED_SEED_SIZE], hash_key[HASH_VALUE_SIZE];
+ _cleanup_free_ struct linux_efi_random_seed *new_seed_table = NULL;
+ struct linux_efi_random_seed *previous_seed_table = NULL;
+ _cleanup_free_ void *seed = NULL, *system_token = NULL;
_cleanup_(file_closep) EFI_FILE *handle = NULL;
- UINTN size, rsize, wsize, system_token_size = 0;
_cleanup_free_ EFI_FILE_INFO *info = NULL;
+ _cleanup_erase_ struct sha256_ctx hash;
uint64_t uefi_monotonic_counter = 0;
+ size_t size, rsize, wsize;
+ bool seeded_by_efi = false;
EFI_STATUS err;
+ EFI_TIME now;
assert(root_dir);
+ assert_cc(DESIRED_SEED_SIZE == HASH_VALUE_SIZE);
validate_sha256();
if (mode == RANDOM_SEED_OFF)
return EFI_NOT_FOUND;
- /* Let's better be safe than sorry, and for now disable this logic in SecureBoot mode, so that we
- * don't credit a random seed that is not authenticated. */
- if (secure_boot_enabled())
- return EFI_NOT_FOUND;
+ /* hash = LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN */
+ sha256_init_ctx(&hash);
+
+ /* Some basic domain separation in case somebody uses this data elsewhere */
+ sha256_process_bytes(HASH_LABEL, sizeof(HASH_LABEL) - 1, &hash);
+
+ for (size_t i = 0; i < ST->NumberOfTableEntries; ++i)
+ if (memcmp(&(const EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID,
+ &ST->ConfigurationTable[i].VendorGuid, sizeof(EFI_GUID)) == 0) {
+ previous_seed_table = ST->ConfigurationTable[i].VendorTable;
+ break;
+ }
+ if (!previous_seed_table) {
+ size = 0;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ } else {
+ size = previous_seed_table->size;
+ seeded_by_efi = size >= DESIRED_SEED_SIZE;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(previous_seed_table->seed, size, &hash);
+
+ /* Zero and free the previous seed table only at the end after we've managed to install a new
+ * one, so that in case this function fails or aborts, Linux still receives whatever the
+ * previous bootloader chain set. So, the next line of this block is not an explicit_bzero()
+ * call. */
+ }
+
+ /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
+ * idea to use it because it helps us for cases where users mistakenly include a random seed in
+ * golden master images that are replicated many times. */
+ err = acquire_rng(random_bytes, sizeof(random_bytes));
+ if (err != EFI_SUCCESS) {
+ size = 0;
+ /* If we can't get any randomness from EFI itself, then we'll only be relying on what's in
+ * ESP. But ESP is mutable, so if secure boot is enabled, we probably shouldn't trust that
+ * alone, in which case we bail out early. */
+ if (!seeded_by_efi && secure_boot_enabled())
+ return EFI_NOT_FOUND;
+ } else {
+ seeded_by_efi = true;
+ size = sizeof(random_bytes);
+ }
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(random_bytes, size, &hash);
/* Get some system specific seed that the installer might have placed in an EFI variable. We include
* it in our hash. This is protection against golden master image sloppiness, and it remains on the
* system, even when disk images are duplicated or swapped out. */
- err = acquire_system_token(&system_token, &system_token_size);
- if (mode != RANDOM_SEED_ALWAYS && err != EFI_SUCCESS)
+ err = acquire_system_token(&system_token, &size);
+ if (mode != RANDOM_SEED_ALWAYS && (err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
return err;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ if (system_token) {
+ sha256_process_bytes(system_token, size, &hash);
+ explicit_bzero_safe(system_token, size);
+ }
err = root_dir->Open(
root_dir,
@@ -261,7 +210,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
err = get_file_info_harder(handle, &info, NULL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to get file info for random seed: %r");
+ return log_error_status_stall(err, L"Failed to get file info for random seed: %r", err);
size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN)
@@ -271,51 +220,105 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
seed = xmalloc(size);
-
rsize = size;
err = handle->Read(handle, &rsize, seed);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
- if (rsize != size)
+ if (rsize != size) {
+ explicit_bzero_safe(seed, rsize);
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
+ }
+
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(seed, size, &hash);
+ explicit_bzero_safe(seed, size);
err = handle->SetPosition(handle, 0);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
- /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
- * idea to use it because it helps us for cases where users mistakenly include a random seed in
- * golden master images that are replicated many times. */
- (void) acquire_rng(size, &rng); /* It's fine if this fails */
-
/* Let's also include the UEFI monotonic counter (which is supposedly increasing on every single
* boot) in the hash, so that even if the changes to the ESP for some reason should not be
* persistent, the random seed we generate will still be different on every single boot. */
err = BS->GetNextMonotonicCount(&uefi_monotonic_counter);
- if (err != EFI_SUCCESS)
+ if (err != EFI_SUCCESS && !seeded_by_efi)
return log_error_status_stall(err, L"Failed to acquire UEFI monotonic counter: %r", err);
-
- /* Calculate new random seed for the disk and what to pass to the kernel */
- err = mangle_random_seed(seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, &new_seed, &for_kernel);
- if (err != EFI_SUCCESS)
- return err;
-
+ size = sizeof(uefi_monotonic_counter);
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
+ err = RT->GetTime(&now, NULL);
+ size = err == EFI_SUCCESS ? sizeof(now) : 0; /* Known to be flaky, so don't bark on error. */
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(&now, size, &hash);
+
+ /* hash_key = HASH(hash) */
+ sha256_finish_ctx(&hash, hash_key);
+
+ /* hash = hash_key || 0 */
+ sha256_init_ctx(&hash);
+ sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+ sha256_process_bytes(&(const uint8_t){ 0 }, sizeof(uint8_t), &hash);
+ /* random_bytes = HASH(hash) */
+ sha256_finish_ctx(&hash, random_bytes);
+
+ size = sizeof(random_bytes);
+ /* If the file size is too large, zero out the remaining bytes on disk, and then truncate. */
+ if (size < info->FileSize) {
+ err = handle->SetPosition(handle, size);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to seek to offset of random seed file: %r", err);
+ wsize = info->FileSize - size;
+ err = handle->Write(handle, &wsize, seed /* All zeros now */);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+ if (wsize != info->FileSize - size)
+ return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+ err = handle->Flush(handle);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+ err = handle->SetPosition(handle, 0);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+ info->FileSize = size;
+ err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+ }
/* Update the random seed on disk before we use it */
wsize = size;
- err = handle->Write(handle, &wsize, new_seed);
+ err = handle->Write(handle, &wsize, random_bytes);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
if (wsize != size)
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
-
err = handle->Flush(handle);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
- /* We are good to go */
- err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
+ err = BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*new_seed_table) + DESIRED_SEED_SIZE,
+ (void **) &new_seed_table);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
+ new_seed_table->size = DESIRED_SEED_SIZE;
+
+ /* hash = hash_key || 1 */
+ sha256_init_ctx(&hash);
+ sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+ sha256_process_bytes(&(const uint8_t){ 1 }, sizeof(uint8_t), &hash);
+ /* new_seed_table->seed = HASH(hash) */
+ sha256_finish_ctx(&hash, new_seed_table->seed);
+
+ err = BS->InstallConfigurationTable(&(EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID, new_seed_table);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
+ return log_error_status_stall(err, L"Failed to install EFI table for random seed: %r", err);
+ TAKE_PTR(new_seed_table);
+
+ if (previous_seed_table) {
+ /* Now that we've succeeded in installing the new table, we can safely nuke the old one. */
+ explicit_bzero_safe(previous_seed_table->seed, previous_seed_table->size);
+ explicit_bzero_safe(previous_seed_table, sizeof(*previous_seed_table));
+ free(previous_seed_table);
+ }
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index b33c50f9fc..15dd87f774 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -10,6 +10,21 @@
#define UINTN_MAX (~(UINTN)0)
#define INTN_MAX ((INTN)(UINTN_MAX>>1))
+#ifdef __OPTIMIZE__
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+#if __has_attribute(__error__)
+__attribute__((noreturn)) extern void __assert_cl_failure__(void) __attribute__((__error__("compile-time assertion failed")));
+#else
+__attribute__((noreturn)) extern void __assert_cl_failure__(void);
+#endif
+/* assert_cl generates a later-stage compile-time assertion when constant folding occurs. */
+#define assert_cl(condition) if (!(condition)) __assert_cl_failure__()
+#else
+#define assert_cl(condition) assert(condition)
+#endif
+
/* gnu-efi format specifiers for integers are fixed to either 64bit with 'l' and 32bit without a size prefix.
* We rely on %u/%d/%x to format regular ints, so ensure the size is what we expect. At the same time, we also
* need specifiers for (U)INTN which are native (pointer) sized. */
@@ -43,6 +58,16 @@ static inline void freep(void *p) {
#define _cleanup_free_ _cleanup_(freep)
+static __always_inline void erase_obj(void *p) {
+ size_t l;
+ assert_cl(p != NULL);
+ l = __builtin_object_size(p, 0);
+ assert_cl(l != (size_t) -1);
+ explicit_bzero_safe(p, l);
+}
+
+#define _cleanup_erase_ _cleanup_(erase_obj)
+
_malloc_ _alloc_(1) _returns_nonnull_ _warn_unused_result_
static inline void *xmalloc(size_t size) {
void *p;