]> git.ipfire.org Git - thirdparty/dhcpcd.git/blob - compat/arc4random.c
d61d61ffa7885ed22f7886246506042625e53db4
[thirdparty/dhcpcd.git] / compat / arc4random.c
1 /* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */
2
3 /*
4 * Copyright (c) 1996, David Mazieres <dm@uun.org>
5 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
6 * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
7 * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 /*
23 * ChaCha based random number generator for OpenBSD.
24 */
25
26 /*
27 * OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c
28 * lib/libc/crypt/arc4random.h
29 */
30
31 #include "config.h"
32
33 #include <fcntl.h>
34 #include <limits.h>
35 #include <signal.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42
43 #if defined(HAVE_OPENSSL)
44 #include <openssl/rand.h>
45 #endif
46
47 #define KEYSTREAM_ONLY
48 #include "chacha_private.h"
49
50 #define minimum(a, b) ((a) < (b) ? (a) : (b))
51
52 #if defined(__GNUC__) || defined(_MSC_VER)
53 #define inline __inline
54 #else /* __GNUC__ || _MSC_VER */
55 #define inline
56 #endif /* !__GNUC__ && !_MSC_VER */
57
58 #define KEYSZ 32
59 #define IVSZ 8
60 #define BLOCKSZ 64
61 #define RSBUFSZ (16*BLOCKSZ)
62
63 #define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
64
65 /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
66 static struct _rs {
67 size_t rs_have; /* valid bytes at end of rs_buf */
68 size_t rs_count; /* bytes till reseed */
69 } *rs;
70
71 /* Maybe be preserved in fork children, if _rs_allocate() decides. */
72 static struct _rsx {
73 chacha_ctx rs_chacha; /* chacha context for random keystream */
74 u_char rs_buf[RSBUFSZ]; /* keystream blocks */
75 } *rsx;
76
77 static int _dhcpcd_rand_fd = -1; /* /dev/urandom fd */
78
79 static int _dhcpcd_getentropy(void *, size_t);
80 static inline int _rs_allocate(struct _rs **, struct _rsx **);
81
82 /* dhcpcd needs to hold onto the fd at fork due to privsep */
83 #if 0
84 static inline void _rs_forkdetect(void);
85 #else
86 #define _rs_forkdetect()
87 #define _rs_forkhandler()
88 #endif
89
90 /* Inline "arc4random.h" */
91 #include <sys/types.h>
92 #include <sys/mman.h>
93
94 static inline void _rs_rekey(u_char *dat, size_t datlen);
95
96 /* dhcpcd isn't multithreaded */
97 #define _ARC4_LOCK()
98 #define _ARC4_UNLOCK()
99
100 static int
101 _dhcpcd_getentropy(void *buf, size_t length)
102 {
103 struct timeval tv;
104 uint8_t *rand = (uint8_t *)buf;
105
106 #if defined (HAVE_OPENSSL)
107 if (RAND_priv_bytes(buf, (int)length) == 1)
108 return (0);
109 #endif
110
111 if (length < sizeof(tv)) {
112 gettimeofday(&tv, NULL);
113 memcpy(buf, &tv, sizeof(tv));
114 length -= sizeof(tv);
115 rand += sizeof(tv);
116 }
117 if (_dhcpcd_rand_fd == -1)
118 _dhcpcd_rand_fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
119 if (_dhcpcd_rand_fd != -1) {
120 /* coverity[check_return] */
121 (void)read(_dhcpcd_rand_fd, rand, length);
122 }
123
124 /* Never fail. If there is an error reading from /dev/urandom,
125 * just use what is on the stack. */
126 return (0);
127 }
128
129 static inline void
130 _getentropy_fail(void)
131 {
132 raise(SIGKILL);
133 }
134
135 #if 0
136 static volatile sig_atomic_t _rs_forked;
137
138 static inline void
139 _rs_forkhandler(void)
140 {
141 _rs_forked = 1;
142 }
143
144 static inline void
145 _rs_forkdetect(void)
146 {
147 static pid_t _rs_pid = 0;
148 pid_t pid = getpid();
149
150 /* XXX unusual calls to clone() can bypass checks */
151 if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
152 _rs_pid = pid;
153 _rs_forked = 0;
154 if (rs)
155 memset(rs, 0, sizeof(*rs));
156 }
157 }
158 #endif
159
160 static inline int
161 _rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
162 {
163 if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
164 MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
165 return (-1);
166
167 if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
168 MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
169 munmap(*rsp, sizeof(**rsp));
170 *rsp = NULL;
171 return (-1);
172 }
173
174 _rs_forkhandler();
175 return (0);
176 }
177
178 static inline void
179 _rs_init(u_char *buf, size_t n)
180 {
181 if (n < KEYSZ + IVSZ)
182 return;
183
184 if (rs == NULL) {
185 if (_rs_allocate(&rs, &rsx) == -1)
186 _exit(1);
187 }
188
189 chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
190 chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
191 }
192
193 static void
194 _rs_stir(void)
195 {
196 u_char rnd[KEYSZ + IVSZ];
197 uint32_t rekey_fuzz = 0;
198
199 if (_dhcpcd_getentropy(rnd, sizeof rnd) == -1)
200 _getentropy_fail();
201
202 if (!rs)
203 _rs_init(rnd, sizeof(rnd));
204 else
205 _rs_rekey(rnd, sizeof(rnd));
206 #if defined(HAVE_EXPLICIT_BZERO)
207 explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
208 #elif defined(HAVE_MEMSET_EXPLICIT)
209 (void)memset_explicit(rnd, 0, sizeof(rnd));
210 #elif defined(HAVE_MEMSET_S)
211 (void)memset_s(rnd, sizeof(rnd), 0, sizeof(rnd));
212 #else
213 #warning potentially insecure use of memset discarding the source seed
214 (void)memset(rnd, 0, sizeof(rnd)); /* discard source seed */
215 #endif
216
217 /* invalidate rs_buf */
218 rs->rs_have = 0;
219 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
220
221 /* rekey interval should not be predictable */
222 chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
223 (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
224 rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
225 }
226
227 static inline void
228 _rs_stir_if_needed(size_t len)
229 {
230 _rs_forkdetect();
231 if (!rs || rs->rs_count <= len)
232 _rs_stir();
233 if (rs->rs_count <= len)
234 rs->rs_count = 0;
235 else
236 rs->rs_count -= len;
237 }
238
239 static inline void
240 _rs_rekey(u_char *dat, size_t datlen)
241 {
242 #ifndef KEYSTREAM_ONLY
243 memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
244 #endif
245 /* fill rs_buf with the keystream */
246 chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
247 rsx->rs_buf, sizeof(rsx->rs_buf));
248 /* mix in optional user provided data */
249 if (dat) {
250 size_t i, m;
251
252 m = minimum(datlen, KEYSZ + IVSZ);
253 for (i = 0; i < m; i++)
254 rsx->rs_buf[i] ^= dat[i];
255 }
256 /* immediately reinit for backtracking resistance */
257 _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
258 memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
259 rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
260 }
261
262 static inline void
263 _rs_random_buf(void *_buf, size_t n)
264 {
265 u_char *buf = (u_char *)_buf;
266 u_char *keystream;
267 size_t m;
268
269 _rs_stir_if_needed(n);
270 while (n > 0) {
271 if (rs->rs_have > 0) {
272 m = minimum(n, rs->rs_have);
273 keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
274 - rs->rs_have;
275 memcpy(buf, keystream, m);
276 memset(keystream, 0, m);
277 buf += m;
278 n -= m;
279 rs->rs_have -= m;
280 }
281 if (rs->rs_have == 0)
282 _rs_rekey(NULL, 0);
283 }
284 }
285
286 static inline void
287 _rs_random_u32(uint32_t *val)
288 {
289 u_char *keystream;
290
291 _rs_stir_if_needed(sizeof(*val));
292 if (rs->rs_have < sizeof(*val))
293 _rs_rekey(NULL, 0);
294 keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
295 memcpy(val, keystream, sizeof(*val));
296 memset(keystream, 0, sizeof(*val));
297 rs->rs_have -= sizeof(*val);
298 }
299
300 uint32_t
301 arc4random(void)
302 {
303 uint32_t val;
304
305 _ARC4_LOCK();
306 _rs_random_u32(&val);
307 _ARC4_UNLOCK();
308 return val;
309 }
310
311 void
312 arc4random_buf(void *buf, size_t n)
313 {
314 _ARC4_LOCK();
315 _rs_random_buf(buf, n);
316 _ARC4_UNLOCK();
317 }