]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: skip random seed handling if seed file is marked read-only
authorLennart Poettering <lennart@amutable.com>
Mon, 13 Jul 2026 13:45:49 +0000 (15:45 +0200)
committerLennart Poettering <lennart@amutable.com>
Tue, 14 Jul 2026 08:12:50 +0000 (10:12 +0200)
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
docs/RANDOM_SEEDS.md
man/systemd-boot.xml
src/boot/random-seed.c

index 484cc4dbf72e1f3cbc3456615a25f72fc153910f..3dd135baf83d6f5a2c82d7df486b6c1c4227003a 100644 (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.
index 2162c7ffb2bba3e611598898b70907ea8d3b78f9..6f8ae2556051b9b3120b43d58c094a4eb00b497e 100644 (file)
     <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
index e10e765b7fe9c4e9d6ba810875e13fb3b3ffa848..ca87073976912cd2868b3b5974e9dca315972da1 100644 (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;
 }