/src/unbound/compat/arc4random.c
Line | Count | Source |
1 | | /* $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 deraadt 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 | | * |
8 | | * Permission to use, copy, modify, and distribute this software for any |
9 | | * purpose with or without fee is hereby granted, provided that the above |
10 | | * copyright notice and this permission notice appear in all copies. |
11 | | * |
12 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 | | */ |
20 | | #include "config.h" |
21 | | |
22 | | /* |
23 | | * ChaCha based random number generator for OpenBSD. |
24 | | */ |
25 | | |
26 | | #include <fcntl.h> |
27 | | #include <limits.h> |
28 | | #include <signal.h> |
29 | | #ifdef HAVE_STDINT_H |
30 | | #include <stdint.h> |
31 | | #endif |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | #include <unistd.h> |
35 | | #include <sys/types.h> |
36 | | #include <sys/param.h> |
37 | | #include <sys/time.h> |
38 | | #ifndef UB_ON_WINDOWS |
39 | | #include <sys/mman.h> |
40 | | #endif |
41 | | #ifdef __QNX__ |
42 | | #include "util/log.h" |
43 | | #endif /* __QNX__ */ |
44 | | |
45 | | #define KEYSTREAM_ONLY |
46 | | #include "chacha_private.h" |
47 | | |
48 | 0 | #define arc4_min(a, b) ((a) < (b) ? (a) : (b)) |
49 | | #ifdef __GNUC__ |
50 | | #define inline __inline |
51 | | #else /* !__GNUC__ */ |
52 | | #define inline |
53 | | #endif /* !__GNUC__ */ |
54 | | #ifndef MAP_ANON |
55 | | #define MAP_ANON MAP_ANONYMOUS |
56 | | #endif |
57 | | |
58 | 0 | #define KEYSZ 32 |
59 | 0 | #define IVSZ 8 |
60 | | #define BLOCKSZ 64 |
61 | | #define RSBUFSZ (16*BLOCKSZ) |
62 | | |
63 | 0 | #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 { |
67 | | size_t rs_have; /* valid bytes at end of rs_buf */ |
68 | | size_t rs_count; /* bytes till reseed */ |
69 | | } *rs; |
70 | | |
71 | | /* Preserved in fork children. */ |
72 | | static struct { |
73 | | chacha_ctx rs_chacha; /* chacha context for random keystream */ |
74 | | u_char rs_buf[RSBUFSZ]; /* keystream blocks */ |
75 | | } *rsx; |
76 | | |
77 | | static inline void _rs_rekey(u_char *dat, size_t datlen); |
78 | | |
79 | | /* |
80 | | * Basic sanity checking; wish we could do better. |
81 | | */ |
82 | | static int |
83 | | fallback_gotdata(char *buf, size_t len) |
84 | 0 | { |
85 | 0 | char any_set = 0; |
86 | 0 | size_t i; |
87 | |
|
88 | 0 | for (i = 0; i < len; ++i) |
89 | 0 | any_set |= buf[i]; |
90 | 0 | if (any_set == 0) |
91 | 0 | return -1; |
92 | 0 | return 0; |
93 | 0 | } |
94 | | |
95 | | /* fallback for getentropy in case libc returns failure */ |
96 | | static int |
97 | | fallback_getentropy_urandom(void *buf, size_t len) |
98 | 0 | { |
99 | 0 | size_t i; |
100 | 0 | int fd, flags; |
101 | 0 | int save_errno = errno; |
102 | |
|
103 | 0 | start: |
104 | |
|
105 | 0 | flags = O_RDONLY; |
106 | 0 | #ifdef O_NOFOLLOW |
107 | 0 | flags |= O_NOFOLLOW; |
108 | 0 | #endif |
109 | 0 | #ifdef O_CLOEXEC |
110 | 0 | flags |= O_CLOEXEC; |
111 | 0 | #endif |
112 | 0 | fd = open("/dev/urandom", flags, 0); |
113 | 0 | if (fd == -1) { |
114 | 0 | if (errno == EINTR) |
115 | 0 | goto start; |
116 | 0 | goto nodevrandom; |
117 | 0 | } |
118 | | #ifndef O_CLOEXEC |
119 | | # ifdef HAVE_FCNTL |
120 | | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); |
121 | | # endif |
122 | | #endif |
123 | 0 | for (i = 0; i < len; ) { |
124 | 0 | size_t wanted = len - i; |
125 | 0 | ssize_t ret = read(fd, (char*)buf + i, wanted); |
126 | |
|
127 | 0 | if (ret == -1) { |
128 | 0 | if (errno == EAGAIN || errno == EINTR) |
129 | 0 | continue; |
130 | 0 | close(fd); |
131 | 0 | goto nodevrandom; |
132 | 0 | } |
133 | 0 | i += ret; |
134 | 0 | } |
135 | 0 | close(fd); |
136 | 0 | if (fallback_gotdata(buf, len) == 0) { |
137 | 0 | errno = save_errno; |
138 | 0 | return 0; /* satisfied */ |
139 | 0 | } |
140 | 0 | nodevrandom: |
141 | 0 | errno = EIO; |
142 | 0 | return -1; |
143 | 0 | } |
144 | | |
145 | | static inline void |
146 | | _rs_init(u_char *buf, size_t n) |
147 | 0 | { |
148 | 0 | assert(buf); |
149 | 0 | if (n < KEYSZ + IVSZ) |
150 | 0 | return; |
151 | | |
152 | 0 | if (rs == NULL) { |
153 | 0 | #ifndef UB_ON_WINDOWS |
154 | 0 | if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE, |
155 | 0 | MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) |
156 | 0 | abort(); |
157 | | #ifdef MAP_INHERIT_ZERO |
158 | | if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1) |
159 | | abort(); |
160 | | #endif |
161 | | #else /* WINDOWS */ |
162 | | rs = malloc(sizeof(*rs)); |
163 | | if(!rs) |
164 | | abort(); |
165 | | #endif |
166 | 0 | } |
167 | 0 | if (rsx == NULL) { |
168 | 0 | #ifndef UB_ON_WINDOWS |
169 | 0 | if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE, |
170 | 0 | MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) |
171 | 0 | abort(); |
172 | | #else /* WINDOWS */ |
173 | | rsx = malloc(sizeof(*rsx)); |
174 | | if(!rsx) |
175 | | abort(); |
176 | | #endif |
177 | 0 | } |
178 | | |
179 | 0 | chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0); |
180 | 0 | chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ); |
181 | 0 | } |
182 | | |
183 | | static void |
184 | | _rs_stir(void) |
185 | 0 | { |
186 | 0 | u_char rnd[KEYSZ + IVSZ]; |
187 | 0 | uint32_t rekey_fuzz = 0; |
188 | |
|
189 | 0 | if (getentropy(rnd, sizeof rnd) == -1) { |
190 | 0 | if(errno != ENOSYS || |
191 | 0 | fallback_getentropy_urandom(rnd, sizeof rnd) == -1) { |
192 | 0 | #ifdef SIGKILL |
193 | 0 | #ifndef __QNX__ |
194 | 0 | raise(SIGKILL); |
195 | | #else /* !__QNX__ */ |
196 | | fatal_exit("failed to getentropy"); |
197 | | #endif /* __QNX__ */ |
198 | | #else |
199 | | exit(9); /* windows */ |
200 | | #endif |
201 | 0 | } |
202 | 0 | } |
203 | |
|
204 | 0 | if (!rs) |
205 | 0 | _rs_init(rnd, sizeof(rnd)); |
206 | 0 | else |
207 | 0 | _rs_rekey(rnd, sizeof(rnd)); |
208 | 0 | explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */ |
209 | | |
210 | | /* invalidate rs_buf */ |
211 | 0 | rs->rs_have = 0; |
212 | 0 | memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); |
213 | | |
214 | | /* rekey interval should not be predictable */ |
215 | 0 | chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz, |
216 | 0 | (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz)); |
217 | 0 | rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE); |
218 | 0 | } |
219 | | |
220 | | static inline void |
221 | | _rs_stir_if_needed(size_t len) |
222 | 0 | { |
223 | 0 | #ifndef MAP_INHERIT_ZERO |
224 | 0 | static pid_t _rs_pid = 0; |
225 | 0 | pid_t pid = getpid(); |
226 | | |
227 | | /* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */ |
228 | 0 | if (_rs_pid == 0 || _rs_pid != pid) { |
229 | 0 | _rs_pid = pid; |
230 | 0 | if (rs) |
231 | 0 | rs->rs_count = 0; |
232 | 0 | } |
233 | 0 | #endif |
234 | 0 | if (!rs || rs->rs_count <= len) |
235 | 0 | _rs_stir(); |
236 | 0 | if (rs->rs_count <= len) |
237 | 0 | rs->rs_count = 0; |
238 | 0 | else |
239 | 0 | rs->rs_count -= len; |
240 | 0 | } |
241 | | |
242 | | static inline void |
243 | | _rs_rekey(u_char *dat, size_t datlen) |
244 | 0 | { |
245 | | #ifndef KEYSTREAM_ONLY |
246 | | memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); |
247 | | #endif |
248 | | /* fill rs_buf with the keystream */ |
249 | 0 | chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf, |
250 | 0 | rsx->rs_buf, sizeof(rsx->rs_buf)); |
251 | | /* mix in optional user provided data */ |
252 | 0 | if (dat) { |
253 | 0 | size_t i, m; |
254 | |
|
255 | 0 | m = arc4_min(datlen, KEYSZ + IVSZ); |
256 | 0 | for (i = 0; i < m; i++) |
257 | 0 | rsx->rs_buf[i] ^= dat[i]; |
258 | 0 | } |
259 | | /* immediately reinit for backtracking resistance */ |
260 | 0 | _rs_init(rsx->rs_buf, KEYSZ + IVSZ); |
261 | 0 | memset(rsx->rs_buf, 0, KEYSZ + IVSZ); |
262 | 0 | rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ; |
263 | 0 | } |
264 | | |
265 | | static inline void |
266 | | _rs_random_buf(void *_buf, size_t n) |
267 | 0 | { |
268 | 0 | u_char *buf = (u_char *)_buf; |
269 | 0 | u_char *keystream; |
270 | 0 | size_t m; |
271 | |
|
272 | 0 | _rs_stir_if_needed(n); |
273 | 0 | while (n > 0) { |
274 | 0 | if (rs->rs_have > 0) { |
275 | 0 | m = arc4_min(n, rs->rs_have); |
276 | 0 | keystream = rsx->rs_buf + sizeof(rsx->rs_buf) |
277 | 0 | - rs->rs_have; |
278 | 0 | memcpy(buf, keystream, m); |
279 | 0 | memset(keystream, 0, m); |
280 | 0 | buf += m; |
281 | 0 | n -= m; |
282 | 0 | rs->rs_have -= m; |
283 | 0 | } |
284 | 0 | if (rs->rs_have == 0) |
285 | 0 | _rs_rekey(NULL, 0); |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | static inline void |
290 | | _rs_random_u32(uint32_t *val) |
291 | 0 | { |
292 | 0 | u_char *keystream; |
293 | 0 | _rs_stir_if_needed(sizeof(*val)); |
294 | 0 | if (rs->rs_have < sizeof(*val)) |
295 | 0 | _rs_rekey(NULL, 0); |
296 | 0 | keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; |
297 | 0 | memcpy(val, keystream, sizeof(*val)); |
298 | 0 | memset(keystream, 0, sizeof(*val)); |
299 | 0 | rs->rs_have -= sizeof(*val); |
300 | 0 | } |
301 | | |
302 | | uint32_t |
303 | | arc4random(void) |
304 | 0 | { |
305 | 0 | uint32_t val; |
306 | |
|
307 | 0 | _ARC4_LOCK(); |
308 | 0 | _rs_random_u32(&val); |
309 | 0 | _ARC4_UNLOCK(); |
310 | 0 | return val; |
311 | 0 | } |
312 | | |
313 | | void |
314 | | arc4random_buf(void *buf, size_t n) |
315 | 0 | { |
316 | 0 | _ARC4_LOCK(); |
317 | 0 | _rs_random_buf(buf, n); |
318 | 0 | _ARC4_UNLOCK(); |
319 | 0 | } |