boot: skip random seed handling if seed file is marked read-only

If the read-only FAT file attribute is set on /loader/random-seed,
don't update the seed file — and hence don't use it either, since a
seed we cannot update would be the same on every boot.

This gives users an explicit way to turn off random seed handling by
marking the file read-only, useful for example in pre-built OS images
that are replicated to many systems, where the baked-in seed is shared
and hence must not be credited.

The check is done upfront in process_random_seed(), before any other
work, mirroring the existing check for read-only volumes. This covers
both systemd-boot and systemd-stub, which share this code.

Inspired-by: #42979
This commit is contained in:
Lennart Poettering
2026-07-13 15:45:49 +02:00
parent 4e67935b77
commit 78f81f02e8
3 changed files with 53 additions and 2 deletions

View File

@@ -216,6 +216,13 @@ boot, in order to ensure the entropy pool is filled up quickly.
too), which should be safe even with FAT file system drivers built into
low-quality EFI firmwares.
If the random seed file has the read-only FAT file attribute set, or if the
ESP file system is read-only as a whole, the seed is neither updated nor
used: a seed that cannot be updated would be identical on every boot, and
hence must not be credited. Setting the read-only file attribute on
`/loader/random-seed` may thus be used to explicitly turn off random seed
handling, for example in OS images that are replicated to multiple systems.
4. A kernel command line option `systemd.random_seed=` may be used to pass in a
base64 encoded seed to initialize the kernel's entropy pool from during
early service manager initialization.

View File

@@ -390,7 +390,10 @@
<filename>/EFI/Linux/</filename> on the ESP and the Extended Boot Loader partition.</para>
<para>Optionally, a random seed for early boot entropy pool provisioning is stored in
<filename>/loader/random-seed</filename> in the ESP.</para>
<filename>/loader/random-seed</filename> in the ESP. If the read-only file attribute is set on this
file, it is neither updated nor used: a seed that cannot be updated would be identical on every boot,
and hence must not be credited. The file attribute may thus be used to explicitly turn off random seed
handling, for example in OS images that are replicated to multiple systems.</para>
<para>During initialization, <command>systemd-boot</command> automatically loads all driver files placed
in the <filename>/EFI/systemd/drivers/</filename> directory of the ESP. The files placed there must have

View File

@@ -21,6 +21,8 @@
/* Some basic domain separation in case somebody uses this data elsewhere */
#define HASH_LABEL "systemd-boot random seed label v1"
#define RANDOM_SEED_PATH u"\\loader\\random-seed"
static EFI_STATUS acquire_rng(void *ret, size_t size) {
EFI_RNG_PROTOCOL *rng;
EFI_STATUS err;
@@ -109,6 +111,31 @@ static void validate_sha256(void) {
#endif
}
static bool random_seed_file_read_only(EFI_FILE *root_dir) {
_cleanup_file_close_ EFI_FILE *handle = NULL;
_cleanup_free_ EFI_FILE_INFO *info = NULL;
EFI_STATUS err;
assert(root_dir);
/* Checks whether the random seed file exists and has the read-only file attribute set. */
err = root_dir->Open(root_dir, &handle, (char16_t *) RANDOM_SEED_PATH, EFI_FILE_MODE_READ, 0);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND)
log_debug_status(err, "Failed to open random seed file for reading, ignoring: %m");
return false;
}
err = get_file_info(handle, &info, /* ret_size= */ NULL);
if (err != EFI_SUCCESS) {
log_debug_status(err, "Failed to get file info of random seed file, ignoring: %m");
return false;
}
return FLAGS_SET(info->Attribute, EFI_FILE_READ_ONLY);
}
EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
uint8_t random_bytes[DESIRED_SEED_SIZE], hash_key[HASH_VALUE_SIZE];
_cleanup_free_ struct linux_efi_random_seed *new_seed_table = NULL;
@@ -143,6 +170,13 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
return EFI_SUCCESS;
}
/* Similarly, if the random seed file is marked read-only, take this as a hint that the seed shall
* not be updated — and hence not be used either, since a seed we cannot update would be the same on
* every boot. This provides a way to explicitly turn off random seed handling, for example for
* pre-built OS images replicated to many systems. */
if (random_seed_file_read_only(root_dir))
goto read_only;
/* hash = LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN */
sha256_init_ctx(&hash);
@@ -200,7 +234,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
err = root_dir->Open(
root_dir,
&handle,
(char16_t *) u"\\loader\\random-seed",
(char16_t *) RANDOM_SEED_PATH,
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0);
if (err == EFI_NOT_FOUND && seeded_by_efi) {
@@ -252,6 +286,9 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
if (err != EFI_SUCCESS)
return log_error_status(err, "Failed to get file info for random seed: %m");
if (FLAGS_SET(info->Attribute, EFI_FILE_READ_ONLY))
goto read_only;
/* Treat a short file just like a freshly created one for robustness reasons: consider a case
* where in a previous run a file was just created and the system was then powered off. In
* such a case the file will already exist, but be too short. */
@@ -387,4 +424,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
}
return EFI_SUCCESS;
read_only:
log_debug("Random seed file is read-only, not updating random seed.");
return EFI_SUCCESS;
}