]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/random-seed/random-seed.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[thirdparty/systemd.git] / src / random-seed / random-seed.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 #include "sd-id128.h"
10
11 #include "alloc-util.h"
12 #include "fd-util.h"
13 #include "io-util.h"
14 #include "log.h"
15 #include "mkdir.h"
16 #include "string-util.h"
17 #include "util.h"
18
19 #define POOL_SIZE_MIN 512
20 #define POOL_SIZE_MAX (10*1024*1024)
21
22 int main(int argc, char *argv[]) {
23 _cleanup_close_ int seed_fd = -1, random_fd = -1;
24 bool read_seed_file, write_seed_file;
25 _cleanup_free_ void* buf = NULL;
26 size_t buf_size = 0;
27 struct stat st;
28 ssize_t k;
29 FILE *f;
30 int r;
31
32 if (argc != 2) {
33 log_error("This program requires one argument.");
34 return EXIT_FAILURE;
35 }
36
37 log_set_target(LOG_TARGET_AUTO);
38 log_parse_environment();
39 log_open();
40
41 umask(0022);
42
43 /* Read pool size, if possible */
44 f = fopen("/proc/sys/kernel/random/poolsize", "re");
45 if (f) {
46 if (fscanf(f, "%zu", &buf_size) > 0)
47 /* poolsize is in bits on 2.6, but we want bytes */
48 buf_size /= 8;
49
50 fclose(f);
51 }
52
53 if (buf_size < POOL_SIZE_MIN)
54 buf_size = POOL_SIZE_MIN;
55
56 r = mkdir_parents_label(RANDOM_SEED, 0755);
57 if (r < 0) {
58 log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
59 goto finish;
60 }
61
62 /* When we load the seed we read it and write it to the device and then immediately update the saved seed with
63 * new data, to make sure the next boot gets seeded differently. */
64
65 if (streq(argv[1], "load")) {
66 int open_rw_error;
67
68 seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
69 open_rw_error = -errno;
70 if (seed_fd < 0) {
71 write_seed_file = false;
72
73 seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY);
74 if (seed_fd < 0) {
75 bool missing = errno == ENOENT;
76
77 log_full_errno(missing ? LOG_DEBUG : LOG_ERR,
78 open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
79 r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR,
80 errno, "Failed to open " RANDOM_SEED " for reading: %m");
81 if (missing)
82 r = 0;
83
84 goto finish;
85 }
86 } else
87 write_seed_file = true;
88
89 random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
90 if (random_fd < 0) {
91 write_seed_file = false;
92
93 random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600);
94 if (random_fd < 0) {
95 r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
96 goto finish;
97 }
98 }
99
100 read_seed_file = true;
101
102 } else if (streq(argv[1], "save")) {
103
104 random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
105 if (random_fd < 0) {
106 r = log_error_errno(errno, "Failed to open /dev/urandom: %m");
107 goto finish;
108 }
109
110 seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
111 if (seed_fd < 0) {
112 r = log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
113 goto finish;
114 }
115
116 read_seed_file = false;
117 write_seed_file = true;
118
119 } else {
120 log_error("Unknown verb '%s'.", argv[1]);
121 r = -EINVAL;
122 goto finish;
123 }
124
125 if (fstat(seed_fd, &st) < 0) {
126 r = log_error_errno(errno, "Failed to stat() seed file " RANDOM_SEED ": %m");
127 goto finish;
128 }
129
130 /* If the seed file is larger than what we expect, then honour the existing size and save/restore as much as it says */
131 if ((uint64_t) st.st_size > buf_size)
132 buf_size = MIN(st.st_size, POOL_SIZE_MAX);
133
134 buf = malloc(buf_size);
135 if (!buf) {
136 r = log_oom();
137 goto finish;
138 }
139
140 if (read_seed_file) {
141 sd_id128_t mid;
142 int z;
143
144 k = loop_read(seed_fd, buf, buf_size, false);
145 if (k < 0)
146 r = log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
147 else if (k == 0) {
148 r = 0;
149 log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding.");
150 } else {
151 (void) lseek(seed_fd, 0, SEEK_SET);
152
153 r = loop_write(random_fd, buf, (size_t) k, false);
154 if (r < 0)
155 log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
156 }
157
158 /* Let's also write the machine ID into the random seed. Why? As an extra protection against "golden
159 * images" that are put together sloppily, i.e. images which are duplicated on multiple systems but
160 * where the random seed file is not properly reset. Frequently the machine ID is properly reset on
161 * those systems however (simply because it's easier to notice, if it isn't due to address clashes and
162 * so on, while random seed equivalence is generally not noticed easily), hence let's simply write the
163 * machined ID into the random pool too. */
164 z = sd_id128_get_machine(&mid);
165 if (z < 0)
166 log_debug_errno(z, "Failed to get machine ID, ignoring: %m");
167 else {
168 z = loop_write(random_fd, &mid, sizeof(mid), false);
169 if (z < 0)
170 log_debug_errno(z, "Failed to write machine ID to /dev/urandom, ignoring: %m");
171 }
172 }
173
174 if (write_seed_file) {
175
176 /* This is just a safety measure. Given that we are root and
177 * most likely created the file ourselves the mode and owner
178 * should be correct anyway. */
179 (void) fchmod(seed_fd, 0600);
180 (void) fchown(seed_fd, 0, 0);
181
182 k = loop_read(random_fd, buf, buf_size, false);
183 if (k < 0) {
184 r = log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
185 goto finish;
186 }
187 if (k == 0) {
188 log_error("Got EOF while reading from /dev/urandom.");
189 r = -EIO;
190 goto finish;
191 }
192
193 r = loop_write(seed_fd, buf, (size_t) k, false);
194 if (r < 0)
195 log_error_errno(r, "Failed to write new random seed file: %m");
196 }
197
198 finish:
199 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
200 }