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