]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
3df3e884 | 2 | |
11c3a366 | 3 | #include <elf.h> |
3df3e884 | 4 | #include <errno.h> |
3df3e884 | 5 | #include <fcntl.h> |
4dd055f9 | 6 | #include <linux/random.h> |
11c3a366 | 7 | #include <stdbool.h> |
dccca82b | 8 | #include <stdint.h> |
11c3a366 | 9 | #include <stdlib.h> |
dccca82b | 10 | #include <string.h> |
4dd055f9 | 11 | #include <sys/ioctl.h> |
11c3a366 | 12 | #include <sys/time.h> |
11c3a366 | 13 | |
349cc4a5 | 14 | #if HAVE_SYS_AUXV_H |
5224c2c7 ZJS |
15 | # include <sys/auxv.h> |
16 | #endif | |
17 | ||
c322f379 | 18 | #include "alloc-util.h" |
fbccb980 | 19 | #include "env-util.h" |
e2b55464 | 20 | #include "errno-util.h" |
3ffd4af2 | 21 | #include "fd-util.h" |
3e155eba | 22 | #include "fileio.h" |
c004493c | 23 | #include "io-util.h" |
4d6222b6 | 24 | #include "iovec-util.h" |
f5947a5e YW |
25 | #include "missing_random.h" |
26 | #include "missing_syscall.h" | |
5545f336 | 27 | #include "missing_threads.h" |
3e155eba | 28 | #include "parse-util.h" |
19b761a0 | 29 | #include "process-util.h" |
3df3e884 | 30 | #include "random-util.h" |
87cb1ab6 | 31 | #include "sha256.h" |
3df3e884 | 32 | #include "time-util.h" |
3df3e884 | 33 | |
582fc142 LP |
34 | /* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by |
35 | * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a | |
36 | * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least | |
37 | * better than libc rand(). */ | |
87cb1ab6 JD |
38 | static void fallback_random_bytes(void *p, size_t n) { |
39 | static thread_local uint64_t fallback_counter = 0; | |
40 | struct { | |
41 | char label[32]; | |
42 | uint64_t call_id, block_id; | |
43 | usec_t stamp_mono, stamp_real; | |
44 | pid_t pid, tid; | |
45 | uint8_t auxval[16]; | |
46 | } state = { | |
47 | /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ | |
48 | .label = "systemd fallback random bytes v1", | |
49 | .call_id = fallback_counter++, | |
50 | .stamp_mono = now(CLOCK_MONOTONIC), | |
51 | .stamp_real = now(CLOCK_REALTIME), | |
19b761a0 | 52 | .pid = getpid_cached(), |
582fc142 | 53 | .tid = gettid(), |
87cb1ab6 | 54 | }; |
a0f11d1d | 55 | |
87cb1ab6 JD |
56 | #if HAVE_SYS_AUXV_H |
57 | memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval)); | |
58 | #endif | |
3df3e884 | 59 | |
87cb1ab6 JD |
60 | while (n > 0) { |
61 | struct sha256_ctx ctx; | |
776cf746 | 62 | |
87cb1ab6 JD |
63 | sha256_init_ctx(&ctx); |
64 | sha256_process_bytes(&state, sizeof(state), &ctx); | |
65 | if (n < SHA256_DIGEST_SIZE) { | |
66 | uint8_t partial[SHA256_DIGEST_SIZE]; | |
67 | sha256_finish_ctx(&ctx, partial); | |
68 | memcpy(p, partial, n); | |
69 | break; | |
31234fbe | 70 | } |
87cb1ab6 JD |
71 | sha256_finish_ctx(&ctx, p); |
72 | p = (uint8_t *) p + SHA256_DIGEST_SIZE; | |
73 | n -= SHA256_DIGEST_SIZE; | |
74 | ++state.block_id; | |
31234fbe | 75 | } |
3df3e884 RC |
76 | } |
77 | ||
87cb1ab6 JD |
78 | void random_bytes(void *p, size_t n) { |
79 | static bool have_getrandom = true, have_grndinsecure = true; | |
254d1313 | 80 | _cleanup_close_ int fd = -EBADF; |
d5a99b7c | 81 | |
87cb1ab6 | 82 | if (n == 0) |
3df3e884 RC |
83 | return; |
84 | ||
87cb1ab6 JD |
85 | for (;;) { |
86 | ssize_t l; | |
87 | ||
88 | if (!have_getrandom) | |
89 | break; | |
90 | ||
91 | l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); | |
92 | if (l > 0) { | |
93 | if ((size_t) l == n) | |
94 | return; /* Done reading, success. */ | |
95 | p = (uint8_t *) p + l; | |
96 | n -= l; | |
97 | continue; /* Interrupted by a signal; keep going. */ | |
98 | } else if (l == 0) | |
99 | break; /* Weird, so fallback to /dev/urandom. */ | |
100 | else if (ERRNO_IS_NOT_SUPPORTED(errno)) { | |
101 | have_getrandom = false; | |
102 | break; /* No syscall, so fallback to /dev/urandom. */ | |
103 | } else if (errno == EINVAL && have_grndinsecure) { | |
104 | have_grndinsecure = false; | |
105 | continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */ | |
106 | } else if (errno == EAGAIN && !have_grndinsecure) | |
107 | break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */ | |
108 | ||
109 | break; /* Unexpected, so just give up and fallback to /dev/urandom. */ | |
a0f11d1d | 110 | } |
6a06b1a5 | 111 | |
87cb1ab6 JD |
112 | fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
113 | if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0) | |
114 | return; | |
85505064 | 115 | |
87cb1ab6 JD |
116 | /* This is a terrible fallback. Oh well. */ |
117 | fallback_random_bytes(p, n); | |
118 | } | |
6a06b1a5 | 119 | |
87cb1ab6 JD |
120 | int crypto_random_bytes(void *p, size_t n) { |
121 | static bool have_getrandom = true, seen_initialized = false; | |
254d1313 | 122 | _cleanup_close_ int fd = -EBADF; |
6a06b1a5 | 123 | |
87cb1ab6 JD |
124 | if (n == 0) |
125 | return 0; | |
6a06b1a5 | 126 | |
87cb1ab6 JD |
127 | for (;;) { |
128 | ssize_t l; | |
129 | ||
130 | if (!have_getrandom) | |
131 | break; | |
132 | ||
133 | l = getrandom(p, n, 0); | |
134 | if (l > 0) { | |
135 | if ((size_t) l == n) | |
136 | return 0; /* Done reading, success. */ | |
137 | p = (uint8_t *) p + l; | |
138 | n -= l; | |
139 | continue; /* Interrupted by a signal; keep going. */ | |
140 | } else if (l == 0) | |
141 | return -EIO; /* Weird, should never happen. */ | |
142 | else if (ERRNO_IS_NOT_SUPPORTED(errno)) { | |
143 | have_getrandom = false; | |
144 | break; /* No syscall, so fallback to /dev/urandom. */ | |
145 | } | |
146 | return -errno; | |
6a06b1a5 | 147 | } |
6a06b1a5 | 148 | |
87cb1ab6 | 149 | if (!seen_initialized) { |
254d1313 | 150 | _cleanup_close_ int ready_fd = -EBADF; |
87cb1ab6 | 151 | int r; |
3df3e884 | 152 | |
87cb1ab6 JD |
153 | ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
154 | if (ready_fd < 0) | |
155 | return -errno; | |
156 | r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY); | |
157 | if (r < 0) | |
158 | return r; | |
159 | seen_initialized = true; | |
160 | } | |
3df3e884 | 161 | |
87cb1ab6 JD |
162 | fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); |
163 | if (fd < 0) | |
164 | return -errno; | |
165 | return loop_read_exact(fd, p, n, false); | |
3df3e884 | 166 | } |
3e155eba | 167 | |
4d6222b6 LP |
168 | int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret) { |
169 | _cleanup_free_ void *p = NULL; | |
170 | int r; | |
171 | ||
172 | assert(ret); | |
173 | ||
174 | p = malloc(MAX(n, 1U)); | |
175 | if (!p) | |
176 | return -ENOMEM; | |
177 | ||
178 | r = crypto_random_bytes(p, n); | |
179 | if (r < 0) | |
180 | return r; | |
181 | ||
182 | *ret = IOVEC_MAKE(TAKE_PTR(p), n); | |
183 | return 0; | |
184 | } | |
185 | ||
3e155eba LP |
186 | size_t random_pool_size(void) { |
187 | _cleanup_free_ char *s = NULL; | |
188 | int r; | |
189 | ||
190 | /* Read pool size, if possible */ | |
191 | r = read_one_line_file("/proc/sys/kernel/random/poolsize", &s); | |
192 | if (r < 0) | |
193 | log_debug_errno(r, "Failed to read pool size from kernel: %m"); | |
194 | else { | |
195 | unsigned sz; | |
196 | ||
197 | r = safe_atou(s, &sz); | |
198 | if (r < 0) | |
199 | log_debug_errno(r, "Failed to parse pool size: %s", s); | |
200 | else | |
201 | /* poolsize is in bits on 2.6, but we want bytes */ | |
202 | return CLAMP(sz / 8, RANDOM_POOL_SIZE_MIN, RANDOM_POOL_SIZE_MAX); | |
203 | } | |
204 | ||
205 | /* Use the minimum as default, if we can't retrieve the correct value */ | |
206 | return RANDOM_POOL_SIZE_MIN; | |
207 | } | |
4dd055f9 LP |
208 | |
209 | int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { | |
254d1313 | 210 | _cleanup_close_ int opened_fd = -EBADF; |
4dd055f9 LP |
211 | int r; |
212 | ||
61bd7d1e LP |
213 | assert(seed || size == 0); |
214 | ||
215 | if (size == 0) | |
216 | return 0; | |
217 | ||
218 | if (fd < 0) { | |
219 | opened_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY); | |
220 | if (opened_fd < 0) | |
221 | return -errno; | |
222 | ||
223 | fd = opened_fd; | |
224 | } | |
4dd055f9 LP |
225 | |
226 | if (credit) { | |
227 | _cleanup_free_ struct rand_pool_info *info = NULL; | |
228 | ||
229 | /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any | |
230 | * chance for confusion here. */ | |
231 | if (size > INT_MAX / 8) | |
232 | return -EOVERFLOW; | |
233 | ||
234 | info = malloc(offsetof(struct rand_pool_info, buf) + size); | |
235 | if (!info) | |
236 | return -ENOMEM; | |
237 | ||
238 | info->entropy_count = size * 8; | |
239 | info->buf_size = size; | |
240 | memcpy(info->buf, seed, size); | |
241 | ||
242 | if (ioctl(fd, RNDADDENTROPY, info) < 0) | |
243 | return -errno; | |
244 | } else { | |
e22c60a9 | 245 | r = loop_write(fd, seed, size); |
4dd055f9 LP |
246 | if (r < 0) |
247 | return r; | |
248 | } | |
249 | ||
61bd7d1e | 250 | return 1; |
4dd055f9 | 251 | } |
5464c961 | 252 | |
1a8900e7 | 253 | uint64_t random_u64_range(uint64_t m) { |
5464c961 LP |
254 | uint64_t x, remainder; |
255 | ||
256 | /* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */ | |
257 | ||
258 | if (m == 0) /* Let's take m == 0 as special case to return an integer from the full range */ | |
259 | return random_u64(); | |
260 | if (m == 1) | |
261 | return 0; | |
262 | ||
263 | remainder = UINT64_MAX % m; | |
264 | ||
265 | do { | |
266 | x = random_u64(); | |
267 | } while (x >= UINT64_MAX - remainder); | |
268 | ||
269 | return x % m; | |
270 | } |