]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include <elf.h> | |
4 | #include <fcntl.h> | |
5 | #include <linux/random.h> | |
6 | #include <string.h> | |
7 | #include <sys/auxv.h> | |
8 | #include <sys/ioctl.h> | |
9 | #include <sys/random.h> | |
10 | #include <threads.h> | |
11 | #include <unistd.h> | |
12 | ||
13 | #include "alloc-util.h" | |
14 | #include "fd-util.h" | |
15 | #include "fileio.h" | |
16 | #include "io-util.h" | |
17 | #include "iovec-util.h" | |
18 | #include "log.h" | |
19 | #include "parse-util.h" | |
20 | #include "pidfd-util.h" | |
21 | #include "process-util.h" | |
22 | #include "random-util.h" | |
23 | #include "sha256.h" | |
24 | #include "time-util.h" | |
25 | ||
26 | /* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by | |
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(). */ | |
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; | |
37 | uint64_t pidfdid; | |
38 | uint8_t auxval[16]; | |
39 | } state = { | |
40 | /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */ | |
41 | .call_id = fallback_counter++, | |
42 | .stamp_mono = now(CLOCK_MONOTONIC), | |
43 | .stamp_real = now(CLOCK_REALTIME), | |
44 | .pid = getpid_cached(), | |
45 | .tid = gettid(), | |
46 | }; | |
47 | ||
48 | memcpy(state.label, "systemd fallback random bytes v1", sizeof(state.label)); | |
49 | memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval)); | |
50 | (void) pidfd_get_inode_id_self_cached(&state.pidfdid); | |
51 | ||
52 | while (n > 0) { | |
53 | struct sha256_ctx ctx; | |
54 | ||
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; | |
62 | } | |
63 | sha256_finish_ctx(&ctx, p); | |
64 | p = (uint8_t *) p + SHA256_DIGEST_SIZE; | |
65 | n -= SHA256_DIGEST_SIZE; | |
66 | ++state.block_id; | |
67 | } | |
68 | } | |
69 | ||
70 | void random_bytes(void *p, size_t n) { | |
71 | static bool have_grndinsecure = true; | |
72 | ||
73 | assert(p || n == 0); | |
74 | ||
75 | if (n == 0) | |
76 | return; | |
77 | ||
78 | for (;;) { | |
79 | ssize_t l; | |
80 | ||
81 | l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK); | |
82 | if (l < 0 && errno == EINVAL && have_grndinsecure) { | |
83 | /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */ | |
84 | have_grndinsecure = false; | |
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. */ | |
90 | ||
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. */ | |
97 | } | |
98 | ||
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) | |
101 | return; | |
102 | ||
103 | /* This is a terrible fallback. Oh well. */ | |
104 | fallback_random_bytes(p, n); | |
105 | } | |
106 | ||
107 | int crypto_random_bytes(void *p, size_t n) { | |
108 | assert(p || n == 0); | |
109 | ||
110 | if (n == 0) | |
111 | return 0; | |
112 | ||
113 | for (;;) { | |
114 | ssize_t l; | |
115 | ||
116 | l = getrandom(p, n, 0); | |
117 | if (l < 0) | |
118 | return -errno; | |
119 | if (l == 0) | |
120 | return -EIO; /* Weird, should never happen. */ | |
121 | ||
122 | if ((size_t) l == n) | |
123 | return 0; /* Done reading, success. */ | |
124 | ||
125 | p = (uint8_t *) p + l; | |
126 | n -= l; | |
127 | /* Interrupted by a signal; keep going. */ | |
128 | } | |
129 | } | |
130 | ||
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 | ||
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 | } | |
171 | ||
172 | int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { | |
173 | _cleanup_close_ int opened_fd = -EBADF; | |
174 | int r; | |
175 | ||
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 | } | |
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 { | |
208 | r = loop_write(fd, seed, size); | |
209 | if (r < 0) | |
210 | return r; | |
211 | } | |
212 | ||
213 | return 1; | |
214 | } | |
215 | ||
216 | uint64_t random_u64_range(uint64_t m) { | |
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 | } |