• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.08
/src/bootctl/bootctl-random-seed.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <unistd.h>
4

5
#include "alloc-util.h"
6
#include "bootctl.h"
7
#include "bootctl-random-seed.h"
8
#include "efivars.h"
9
#include "env-util.h"
10
#include "fd-util.h"
11
#include "find-esp.h"
12
#include "fs-util.h"
13
#include "glyph-util.h"
14
#include "io-util.h"
15
#include "log.h"
16
#include "random-util.h"
17
#include "sha256.h"
18
#include "tmpfile-util.h"
19
#include "umask-util.h"
20

21
static int random_seed_verify_permissions(int fd, mode_t expected_type) {
57✔
22
        _cleanup_free_ char *full_path = NULL;
57✔
23
        struct stat st;
57✔
24
        int r;
57✔
25

26
        assert(fd >= 0);
57✔
27

28
        r = fd_get_path(fd, &full_path);
57✔
29
        if (r < 0)
57✔
30
                return log_error_errno(r, "Unable to determine full path of random seed fd: %m");
×
31

32
        if (fstat(fd, &st) < 0)
57✔
33
                return log_error_errno(errno, "Unable to stat %s: %m", full_path);
×
34

35
        if (((st.st_mode ^ expected_type) & S_IFMT) != 0)
57✔
36
                return log_error_errno(SYNTHETIC_ERRNO(EBADF),
×
37
                                       "Unexpected inode type when validating random seed access mode on '%s'.", full_path);
38

39
        if ((st.st_mode & 0007) == 0) /* All world bits are off? Then all is good */
57✔
40
                return 0;
41

42
        if (S_ISREG(expected_type))
10✔
43
                log_error("%s%sRandom seed file '%s' is world accessible, which is a security hole!%s%s",
5✔
44
                          optional_glyph(GLYPH_WARNING_SIGN), optional_glyph(GLYPH_SPACE),
45
                          full_path,
46
                          optional_glyph(GLYPH_SPACE), optional_glyph(GLYPH_WARNING_SIGN));
47
        else
48
                log_error("%s%s Mount point '%s' which backs the random seed file is world accessible, which is a security hole! %s%s",
5✔
49
                          optional_glyph(GLYPH_WARNING_SIGN), optional_glyph(GLYPH_SPACE),
50
                          full_path,
51
                          optional_glyph(GLYPH_SPACE), optional_glyph(GLYPH_WARNING_SIGN));
52

53
        return 1;
54
}
55

56
static int set_system_token(void) {
25✔
57
        uint8_t buffer[RANDOM_EFI_SEED_SIZE];
25✔
58
        size_t token_size;
25✔
59
        int r;
25✔
60

61
        if (!touch_variables())
25✔
62
                return 0;
25✔
63

64
        r = secure_getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
15✔
65
        if (r < 0) {
15✔
66
                if (r != -ENXIO)
15✔
67
                        log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
×
68
        } else if (r == 0) {
×
69
                log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
×
70
                return 0;
×
71
        }
72

73
        r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderSystemToken"), NULL, NULL, &token_size);
15✔
74
        if (r == -ENODATA)
15✔
75
                log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
×
76
        else if (r < 0) {
15✔
77
                if (r != -ENOENT)
8✔
78
                        return log_error_errno(r, "Failed to test system token validity: %m");
×
79
        } else {
80
                if (token_size >= sizeof(buffer)) {
7✔
81
                        /* Let's avoid writes if we can, and initialize this only once. */
82
                        log_debug("System token already written, not updating.");
7✔
83
                        return 0;
7✔
84
                }
85

86
                log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
×
87
        }
88

89
        r = crypto_random_bytes(buffer, sizeof(buffer));
8✔
90
        if (r < 0)
8✔
91
                return log_error_errno(r, "Failed to acquire random seed: %m");
×
92

93
        /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
94
         * and possibly get identification information or too much insight into the kernel's entropy pool
95
         * state. */
96
        WITH_UMASK(0077) {
16✔
97
                r = efi_set_variable(EFI_LOADER_VARIABLE_STR("LoaderSystemToken"), buffer, sizeof(buffer));
8✔
98
                if (r < 0) {
8✔
99
                        if (arg_graceful() == ARG_GRACEFUL_NO)
×
100
                                return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
×
101

102
                        if (r == -EINVAL)
×
103
                                log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
×
104
                        else
105
                                log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
8✔
106
                } else
107
                        log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
8✔
108
        }
109

110
        return 0;
8✔
111
}
112

113
int install_random_seed(const char *esp) {
25✔
114
        _cleanup_close_ int esp_fd = -EBADF, loader_dir_fd = -EBADF, fd = -EBADF;
50✔
115
        _cleanup_free_ char *tmp = NULL;
25✔
116
        uint8_t buffer[RANDOM_EFI_SEED_SIZE];
25✔
117
        struct sha256_ctx hash_state;
25✔
118
        bool refreshed, warned = false;
25✔
119
        int r;
25✔
120

121
        assert(esp);
25✔
122

123
        assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
25✔
124

125
        if (!arg_install_random_seed)
25✔
126
                return 0;
127

128
        esp_fd = open(esp, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
25✔
129
        if (esp_fd < 0)
25✔
130
                return log_error_errno(errno, "Failed to open ESP directory '%s': %m", esp);
×
131

132
        (void) random_seed_verify_permissions(esp_fd, S_IFDIR);
25✔
133

134
        loader_dir_fd = open_mkdir_at(esp_fd, "loader", O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOFOLLOW, 0775);
25✔
135
        if (loader_dir_fd < 0)
25✔
136
                return log_error_errno(loader_dir_fd, "Failed to open loader directory '%s/loader': %m", esp);
×
137

138
        r = crypto_random_bytes(buffer, sizeof(buffer));
25✔
139
        if (r < 0)
25✔
140
                return log_error_errno(r, "Failed to acquire random seed: %m");
×
141

142
        sha256_init_ctx(&hash_state);
25✔
143
        sha256_process_bytes_and_size(buffer, sizeof(buffer), &hash_state);
25✔
144

145
        fd = openat(loader_dir_fd, "random-seed", O_NOFOLLOW|O_CLOEXEC|O_RDONLY|O_NOCTTY);
25✔
146
        if (fd < 0) {
25✔
147
                if (errno != ENOENT)
17✔
148
                        return log_error_errno(errno, "Failed to open old random seed file: %m");
×
149

150
                sha256_process_bytes(&(const ssize_t) { 0 }, sizeof(ssize_t), &hash_state);
17✔
151
                refreshed = false;
17✔
152
        } else {
153
                ssize_t n;
8✔
154

155
                warned = random_seed_verify_permissions(fd, S_IFREG) > 0;
8✔
156

157
                /* Hash the old seed in so that we never regress in entropy. */
158

159
                n = read(fd, buffer, sizeof(buffer));
8✔
160
                if (n < 0)
8✔
161
                        return log_error_errno(errno, "Failed to read old random seed file: %m");
×
162

163
                sha256_process_bytes_and_size(buffer, n, &hash_state);
8✔
164

165
                fd = safe_close(fd);
8✔
166
                refreshed = n > 0;
8✔
167
        }
168

169
        sha256_finish_ctx(&hash_state, buffer);
25✔
170

171
        if (tempfn_random("random-seed", "bootctl", &tmp) < 0)
25✔
172
                return log_oom();
×
173

174
        fd = openat(loader_dir_fd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
25✔
175
        if (fd < 0)
25✔
176
                return log_error_errno(fd, "Failed to open random seed file for writing: %m");
×
177

178
        if (!warned) /* only warn once per seed file */
25✔
179
                (void) random_seed_verify_permissions(fd, S_IFREG);
24✔
180

181
        r = loop_write(fd, buffer, sizeof(buffer));
25✔
182
        if (r < 0) {
25✔
183
                log_error_errno(r, "Failed to write random seed file: %m");
×
184
                goto fail;
×
185
        }
186

187
        if (fsync(fd) < 0 || fsync(loader_dir_fd) < 0) {
25✔
188
                r = log_error_errno(errno, "Failed to sync random seed file: %m");
×
189
                goto fail;
×
190
        }
191

192
        if (renameat(loader_dir_fd, tmp, loader_dir_fd, "random-seed") < 0) {
25✔
193
                r = log_error_errno(errno, "Failed to move random seed file into place: %m");
×
194
                goto fail;
×
195
        }
196

197
        tmp = mfree(tmp);
25✔
198

199
        if (syncfs(fd) < 0)
25✔
200
                return log_error_errno(errno, "Failed to sync ESP file system: %m");
×
201

202
        log_info("Random seed file %s/loader/random-seed successfully %s (%zu bytes).", esp, refreshed ? "refreshed" : "written", sizeof(buffer));
42✔
203

204
        return set_system_token();
25✔
205

206
fail:
×
207
        assert(tmp);
×
208
        (void) unlinkat(loader_dir_fd, tmp, 0);
×
209

210
        return r;
×
211
}
212

213
int verb_random_seed(int argc, char *argv[], void *userdata) {
12✔
214
        int r;
12✔
215

216
        r = find_esp_and_warn(arg_root, arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
12✔
217
        if (r == -ENOKEY) {
12✔
218
                /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
219
                if (arg_graceful() == ARG_GRACEFUL_NO)
×
220
                        return log_error_errno(r, "Unable to find ESP.");
×
221

222
                log_notice("No ESP found, not initializing random seed.");
×
223
                return 0;
×
224
        }
225
        if (r < 0)
12✔
226
                return r;
227

228
        r = install_random_seed(arg_esp_path);
12✔
229
        if (r < 0)
12✔
230
                return r;
×
231

232
        return 0;
233
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc