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