1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <fcntl.h>
4 #include <unistd.h>
5 
6 #include "alloc-util.h"
7 #include "chattr-util.h"
8 #include "efi-random.h"
9 #include "efivars.h"
10 #include "fd-util.h"
11 #include "fs-util.h"
12 #include "random-util.h"
13 #include "strv.h"
14 
15 /* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
16  * the kernel's random pool, but only once per boot. If this is run very early during initialization we can
17  * instantly boot up with a filled random pool.
18  *
19  * This makes no judgement on the entropy passed, it's the job of the boot loader to only pass us a seed that
20  * is suitably validated. */
21 
lock_down_efi_variables(void)22 static void lock_down_efi_variables(void) {
23         int r;
24 
25         /* Paranoia: let's restrict access modes of these a bit, so that unprivileged users can't use them to
26          * identify the system or gain too much insight into what we might have credited to the entropy
27          * pool. */
28         FOREACH_STRING(path,
29                        EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)),
30                        EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken))) {
31 
32                 r = chattr_path(path, 0, FS_IMMUTABLE_FL, NULL);
33                 if (r == -ENOENT)
34                         continue;
35                 if (r < 0)
36                         log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", path);
37 
38                 if (chmod(path, 0600) < 0)
39                         log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", path);
40         }
41 }
42 
efi_take_random_seed(void)43 int efi_take_random_seed(void) {
44         _cleanup_free_ void *value = NULL;
45         size_t size;
46         int r;
47 
48         /* Paranoia comes first. */
49         lock_down_efi_variables();
50 
51         if (access("/run/systemd/efi-random-seed-taken", F_OK) < 0) {
52                 if (errno != ENOENT) {
53                         log_warning_errno(errno, "Failed to determine whether we already used the random seed token, not using it.");
54                         return 0;
55                 }
56 
57                 /* ENOENT means we haven't used it yet. */
58         } else {
59                 log_debug("EFI random seed already used, not using again.");
60                 return 0;
61         }
62 
63         r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderRandomSeed), NULL, &value, &size);
64         if (r == -EOPNOTSUPP) {
65                 log_debug_errno(r, "System lacks EFI support, not initializing random seed from EFI variable.");
66                 return 0;
67         }
68         if (r == -ENOENT) {
69                 log_debug_errno(r, "Boot loader did not pass LoaderRandomSeed EFI variable, not crediting any entropy.");
70                 return 0;
71         }
72         if (r < 0)
73                 return log_warning_errno(r, "Failed to read LoaderRandomSeed EFI variable, ignoring: %m");
74 
75         if (size == 0)
76                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
77 
78         /* Before we use the seed, let's mark it as used, so that we never credit it twice. Also, it's a nice
79          * way to let users known that we successfully acquired entropy from the boot laoder. */
80         r = touch("/run/systemd/efi-random-seed-taken");
81         if (r < 0)
82                 return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
83 
84         r = random_write_entropy(-1, value, size, true);
85         if (r < 0)
86                 return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
87 
88         log_info("Successfully credited entropy passed from boot loader.");
89         return 1;
90 }
91