]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/random-util.c
ask-password-api: Add more debug logging
[thirdparty/systemd.git] / src / basic / random-util.c
CommitLineData
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
30static 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 70void 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 107int 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
131int 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
149size_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
172int 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 216uint64_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}