]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* $OpenBSD: getentropy_linux.c,v 1.20 2014/07/12 15:43:49 beck Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org> | |
5 | * Copyright (c) 2014 Bob Beck <beck@obtuse.com> | |
6 | * | |
7 | * Permission to use, copy, modify, and distribute this software for any | |
8 | * purpose with or without fee is hereby granted, provided that the above | |
9 | * copyright notice and this permission notice appear in all copies. | |
10 | * | |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | */ | |
19 | #include "config.h" | |
20 | ||
21 | /* | |
22 | #define _POSIX_C_SOURCE 199309L | |
23 | #define _GNU_SOURCE 1 | |
24 | */ | |
25 | #include <sys/types.h> | |
26 | #include <sys/param.h> | |
27 | #include <sys/ioctl.h> | |
28 | #include <sys/resource.h> | |
29 | #include <sys/syscall.h> | |
30 | #ifdef HAVE_SYS_SYSCTL_H | |
31 | #include <sys/sysctl.h> | |
32 | #endif | |
33 | #include <sys/statvfs.h> | |
34 | #include <sys/socket.h> | |
35 | #include <sys/mount.h> | |
36 | #include <sys/mman.h> | |
37 | #include <sys/stat.h> | |
38 | #include <sys/time.h> | |
39 | #include <stdlib.h> | |
40 | #include <stdint.h> | |
41 | #include <stdio.h> | |
42 | #include <termios.h> | |
43 | #include <fcntl.h> | |
44 | #include <signal.h> | |
45 | #include <string.h> | |
46 | #include <errno.h> | |
47 | #include <unistd.h> | |
48 | #include <time.h> | |
49 | #include <openssl/sha.h> | |
50 | ||
51 | #include <linux/types.h> | |
52 | #include <linux/random.h> | |
53 | #include <linux/sysctl.h> | |
54 | #ifdef HAVE_GETAUXVAL | |
55 | #include <sys/auxv.h> | |
56 | #endif | |
57 | #include <sys/vfs.h> | |
58 | ||
59 | #define REPEAT 5 | |
60 | #define min(a, b) (((a) < (b)) ? (a) : (b)) | |
61 | ||
62 | #define HX(a, b) \ | |
63 | do { \ | |
64 | if ((a)) \ | |
65 | HD(errno); \ | |
66 | else \ | |
67 | HD(b); \ | |
68 | } while (0) | |
69 | ||
70 | #define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l))) | |
71 | #define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x))) | |
72 | #define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*))) | |
73 | ||
74 | int getentropy(void *buf, size_t len); | |
75 | ||
76 | #ifdef CAN_REFERENCE_MAIN | |
77 | extern int main(int, char *argv[]); | |
78 | #endif | |
79 | static int gotdata(char *buf, size_t len); | |
80 | static int getentropy_urandom(void *buf, size_t len); | |
81 | #ifdef SYS__sysctl | |
82 | static int getentropy_sysctl(void *buf, size_t len); | |
83 | #endif | |
84 | static int getentropy_fallback(void *buf, size_t len); | |
85 | ||
86 | int | |
87 | getentropy(void *buf, size_t len) | |
88 | { | |
89 | int ret = -1; | |
90 | ||
91 | if (len > 256) { | |
92 | errno = EIO; | |
93 | return -1; | |
94 | } | |
95 | ||
96 | /* | |
97 | * Try to get entropy with /dev/urandom | |
98 | * | |
99 | * This can fail if the process is inside a chroot or if file | |
100 | * descriptors are exhausted. | |
101 | */ | |
102 | ret = getentropy_urandom(buf, len); | |
103 | if (ret != -1) | |
104 | return (ret); | |
105 | ||
106 | #ifdef SYS__sysctl | |
107 | /* | |
108 | * Try to use sysctl CTL_KERN, KERN_RANDOM, RANDOM_UUID. | |
109 | * sysctl is a failsafe API, so it guarantees a result. This | |
110 | * should work inside a chroot, or when file descriptors are | |
111 | * exhuasted. | |
112 | * | |
113 | * However this can fail if the Linux kernel removes support | |
114 | * for sysctl. Starting in 2007, there have been efforts to | |
115 | * deprecate the sysctl API/ABI, and push callers towards use | |
116 | * of the chroot-unavailable fd-using /proc mechanism -- | |
117 | * essentially the same problems as /dev/urandom. | |
118 | * | |
119 | * Numerous setbacks have been encountered in their deprecation | |
120 | * schedule, so as of June 2014 the kernel ABI still exists on | |
121 | * most Linux architectures. The sysctl() stub in libc is missing | |
122 | * on some systems. There are also reports that some kernels | |
123 | * spew messages to the console. | |
124 | */ | |
125 | ret = getentropy_sysctl(buf, len); | |
126 | if (ret != -1) | |
127 | return (ret); | |
128 | #endif /* SYS__sysctl */ | |
129 | ||
130 | /* | |
131 | * Entropy collection via /dev/urandom and sysctl have failed. | |
132 | * | |
133 | * No other API exists for collecting entropy. See the large | |
134 | * comment block above. | |
135 | * | |
136 | * We have very few options: | |
137 | * - Even syslog_r is unsafe to call at this low level, so | |
138 | * there is no way to alert the user or program. | |
139 | * - Cannot call abort() because some systems have unsafe | |
140 | * corefiles. | |
141 | * - Could raise(SIGKILL) resulting in silent program termination. | |
142 | * - Return EIO, to hint that arc4random's stir function | |
143 | * should raise(SIGKILL) | |
144 | * - Do the best under the circumstances.... | |
145 | * | |
146 | * This code path exists to bring light to the issue that Linux | |
147 | * does not provide a failsafe API for entropy collection. | |
148 | * | |
149 | * We hope this demonstrates that Linux should either retain their | |
150 | * sysctl ABI, or consider providing a new failsafe API which | |
151 | * works in a chroot or when file descriptors are exhausted. | |
152 | */ | |
153 | #undef FAIL_INSTEAD_OF_TRYING_FALLBACK | |
154 | #ifdef FAIL_INSTEAD_OF_TRYING_FALLBACK | |
155 | raise(SIGKILL); | |
156 | #endif | |
157 | ret = getentropy_fallback(buf, len); | |
158 | if (ret != -1) | |
159 | return (ret); | |
160 | ||
161 | errno = EIO; | |
162 | return (ret); | |
163 | } | |
164 | ||
165 | /* | |
166 | * Basic sanity checking; wish we could do better. | |
167 | */ | |
168 | static int | |
169 | gotdata(char *buf, size_t len) | |
170 | { | |
171 | char any_set = 0; | |
172 | size_t i; | |
173 | ||
174 | for (i = 0; i < len; ++i) | |
175 | any_set |= buf[i]; | |
176 | if (any_set == 0) | |
177 | return -1; | |
178 | return 0; | |
179 | } | |
180 | ||
181 | static int | |
182 | getentropy_urandom(void *buf, size_t len) | |
183 | { | |
184 | struct stat st; | |
185 | size_t i; | |
186 | int fd, cnt, flags; | |
187 | int save_errno = errno; | |
188 | ||
189 | start: | |
190 | ||
191 | flags = O_RDONLY; | |
192 | #ifdef O_NOFOLLOW | |
193 | flags |= O_NOFOLLOW; | |
194 | #endif | |
195 | #ifdef O_CLOEXEC | |
196 | flags |= O_CLOEXEC; | |
197 | #endif | |
198 | fd = open("/dev/urandom", flags, 0); | |
199 | if (fd == -1) { | |
200 | if (errno == EINTR) | |
201 | goto start; | |
202 | goto nodevrandom; | |
203 | } | |
204 | #ifndef O_CLOEXEC | |
205 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); | |
206 | #endif | |
207 | ||
208 | /* Lightly verify that the device node looks sane */ | |
209 | if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { | |
210 | close(fd); | |
211 | goto nodevrandom; | |
212 | } | |
213 | if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) { | |
214 | close(fd); | |
215 | goto nodevrandom; | |
216 | } | |
217 | for (i = 0; i < len; ) { | |
218 | size_t wanted = len - i; | |
219 | ssize_t ret = read(fd, (char*)buf + i, wanted); | |
220 | ||
221 | if (ret == -1) { | |
222 | if (errno == EAGAIN || errno == EINTR) | |
223 | continue; | |
224 | close(fd); | |
225 | goto nodevrandom; | |
226 | } | |
227 | i += ret; | |
228 | } | |
229 | close(fd); | |
230 | if (gotdata(buf, len) == 0) { | |
231 | errno = save_errno; | |
232 | return 0; /* satisfied */ | |
233 | } | |
234 | nodevrandom: | |
235 | errno = EIO; | |
236 | return -1; | |
237 | } | |
238 | ||
239 | #ifdef SYS__sysctl | |
240 | static int | |
241 | getentropy_sysctl(void *buf, size_t len) | |
242 | { | |
243 | static int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID }; | |
244 | size_t i; | |
245 | int save_errno = errno; | |
246 | ||
247 | for (i = 0; i < len; ) { | |
248 | size_t chunk = min(len - i, 16); | |
249 | ||
250 | /* SYS__sysctl because some systems already removed sysctl() */ | |
251 | struct __sysctl_args args = { | |
252 | .name = mib, | |
253 | .nlen = 3, | |
254 | .oldval = buf + i, | |
255 | .oldlenp = &chunk, | |
256 | }; | |
257 | if (syscall(SYS__sysctl, &args) != 0) | |
258 | goto sysctlfailed; | |
259 | i += chunk; | |
260 | } | |
261 | if (gotdata(buf, len) == 0) { | |
262 | errno = save_errno; | |
263 | return (0); /* satisfied */ | |
264 | } | |
265 | sysctlfailed: | |
266 | errno = EIO; | |
267 | return -1; | |
268 | } | |
269 | #endif /* SYS__sysctl */ | |
270 | ||
271 | static int cl[] = { | |
272 | CLOCK_REALTIME, | |
273 | #ifdef CLOCK_MONOTONIC | |
274 | CLOCK_MONOTONIC, | |
275 | #endif | |
276 | #ifdef CLOCK_MONOTONIC_RAW | |
277 | CLOCK_MONOTONIC_RAW, | |
278 | #endif | |
279 | #ifdef CLOCK_TAI | |
280 | CLOCK_TAI, | |
281 | #endif | |
282 | #ifdef CLOCK_VIRTUAL | |
283 | CLOCK_VIRTUAL, | |
284 | #endif | |
285 | #ifdef CLOCK_UPTIME | |
286 | CLOCK_UPTIME, | |
287 | #endif | |
288 | #ifdef CLOCK_PROCESS_CPUTIME_ID | |
289 | CLOCK_PROCESS_CPUTIME_ID, | |
290 | #endif | |
291 | #ifdef CLOCK_THREAD_CPUTIME_ID | |
292 | CLOCK_THREAD_CPUTIME_ID, | |
293 | #endif | |
294 | }; | |
295 | ||
296 | static int | |
297 | getentropy_fallback(void *buf, size_t len) | |
298 | { | |
299 | uint8_t results[SHA512_DIGEST_LENGTH]; | |
300 | int save_errno = errno, e, pgs = getpagesize(), faster = 0, repeat; | |
301 | static int cnt; | |
302 | struct timespec ts; | |
303 | struct timeval tv; | |
304 | struct rusage ru; | |
305 | sigset_t sigset; | |
306 | struct stat st; | |
307 | SHA512_CTX ctx; | |
308 | static pid_t lastpid; | |
309 | pid_t pid; | |
310 | size_t i, ii, m; | |
311 | char *p; | |
312 | ||
313 | pid = getpid(); | |
314 | if (lastpid == pid) { | |
315 | faster = 1; | |
316 | repeat = 2; | |
317 | } else { | |
318 | faster = 0; | |
319 | lastpid = pid; | |
320 | repeat = REPEAT; | |
321 | } | |
322 | for (i = 0; i < len; ) { | |
323 | int j; | |
324 | SHA512_Init(&ctx); | |
325 | for (j = 0; j < repeat; j++) { | |
326 | HX((e = gettimeofday(&tv, NULL)) == -1, tv); | |
327 | if (e != -1) { | |
328 | cnt += (int)tv.tv_sec; | |
329 | cnt += (int)tv.tv_usec; | |
330 | } | |
331 | ||
332 | for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++) | |
333 | HX(clock_gettime(cl[ii], &ts) == -1, ts); | |
334 | ||
335 | HX((pid = getpid()) == -1, pid); | |
336 | HX((pid = getsid(pid)) == -1, pid); | |
337 | HX((pid = getppid()) == -1, pid); | |
338 | HX((pid = getpgid(0)) == -1, pid); | |
339 | HX((e = getpriority(0, 0)) == -1, e); | |
340 | ||
341 | if (!faster) { | |
342 | ts.tv_sec = 0; | |
343 | ts.tv_nsec = 1; | |
344 | (void) nanosleep(&ts, NULL); | |
345 | } | |
346 | ||
347 | HX(sigpending(&sigset) == -1, sigset); | |
348 | HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, | |
349 | sigset); | |
350 | ||
351 | #ifdef CAN_REFERENCE_MAIN | |
352 | HF(main); /* an addr in program */ | |
353 | #endif | |
354 | HF(getentropy); /* an addr in this library */ | |
355 | HF(printf); /* an addr in libc */ | |
356 | p = (char *)&p; | |
357 | HD(p); /* an addr on stack */ | |
358 | p = (char *)&errno; | |
359 | HD(p); /* the addr of errno */ | |
360 | ||
361 | if (i == 0) { | |
362 | struct sockaddr_storage ss; | |
363 | struct statvfs stvfs; | |
364 | struct termios tios; | |
365 | struct statfs stfs; | |
366 | socklen_t ssl; | |
367 | off_t off; | |
368 | ||
369 | /* | |
370 | * Prime-sized mappings encourage fragmentation; | |
371 | * thus exposing some address entropy. | |
372 | */ | |
373 | struct mm { | |
374 | size_t npg; | |
375 | void *p; | |
376 | } mm[] = { | |
377 | { 17, MAP_FAILED }, { 3, MAP_FAILED }, | |
378 | { 11, MAP_FAILED }, { 2, MAP_FAILED }, | |
379 | { 5, MAP_FAILED }, { 3, MAP_FAILED }, | |
380 | { 7, MAP_FAILED }, { 1, MAP_FAILED }, | |
381 | { 57, MAP_FAILED }, { 3, MAP_FAILED }, | |
382 | { 131, MAP_FAILED }, { 1, MAP_FAILED }, | |
383 | }; | |
384 | ||
385 | for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { | |
386 | HX(mm[m].p = mmap(NULL, | |
387 | mm[m].npg * pgs, | |
388 | PROT_READ|PROT_WRITE, | |
389 | MAP_PRIVATE|MAP_ANON, -1, | |
390 | (off_t)0), mm[m].p); | |
391 | if (mm[m].p != MAP_FAILED) { | |
392 | size_t mo; | |
393 | ||
394 | /* Touch some memory... */ | |
395 | p = mm[m].p; | |
396 | mo = cnt % | |
397 | (mm[m].npg * pgs - 1); | |
398 | p[mo] = 1; | |
399 | cnt += (int)((long)(mm[m].p) | |
400 | / pgs); | |
401 | } | |
402 | ||
403 | /* Check cnts and times... */ | |
404 | for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); | |
405 | ii++) { | |
406 | HX((e = clock_gettime(cl[ii], | |
407 | &ts)) == -1, ts); | |
408 | if (e != -1) | |
409 | cnt += (int)ts.tv_nsec; | |
410 | } | |
411 | ||
412 | HX((e = getrusage(RUSAGE_SELF, | |
413 | &ru)) == -1, ru); | |
414 | if (e != -1) { | |
415 | cnt += (int)ru.ru_utime.tv_sec; | |
416 | cnt += (int)ru.ru_utime.tv_usec; | |
417 | } | |
418 | } | |
419 | ||
420 | for (m = 0; m < sizeof mm/sizeof(mm[0]); m++) { | |
421 | if (mm[m].p != MAP_FAILED) | |
422 | munmap(mm[m].p, mm[m].npg * pgs); | |
423 | mm[m].p = MAP_FAILED; | |
424 | } | |
425 | ||
426 | HX(stat(".", &st) == -1, st); | |
427 | HX(statvfs(".", &stvfs) == -1, stvfs); | |
428 | HX(statfs(".", &stfs) == -1, stfs); | |
429 | ||
430 | HX(stat("/", &st) == -1, st); | |
431 | HX(statvfs("/", &stvfs) == -1, stvfs); | |
432 | HX(statfs("/", &stfs) == -1, stfs); | |
433 | ||
434 | HX((e = fstat(0, &st)) == -1, st); | |
435 | if (e == -1) { | |
436 | if (S_ISREG(st.st_mode) || | |
437 | S_ISFIFO(st.st_mode) || | |
438 | S_ISSOCK(st.st_mode)) { | |
439 | HX(fstatvfs(0, &stvfs) == -1, | |
440 | stvfs); | |
441 | HX(fstatfs(0, &stfs) == -1, | |
442 | stfs); | |
443 | HX((off = lseek(0, (off_t)0, | |
444 | SEEK_CUR)) < 0, off); | |
445 | } | |
446 | if (S_ISCHR(st.st_mode)) { | |
447 | HX(tcgetattr(0, &tios) == -1, | |
448 | tios); | |
449 | } else if (S_ISSOCK(st.st_mode)) { | |
450 | memset(&ss, 0, sizeof ss); | |
451 | ssl = sizeof(ss); | |
452 | HX(getpeername(0, | |
453 | (void *)&ss, &ssl) == -1, | |
454 | ss); | |
455 | } | |
456 | } | |
457 | ||
458 | HX((e = getrusage(RUSAGE_CHILDREN, | |
459 | &ru)) == -1, ru); | |
460 | if (e != -1) { | |
461 | cnt += (int)ru.ru_utime.tv_sec; | |
462 | cnt += (int)ru.ru_utime.tv_usec; | |
463 | } | |
464 | } else { | |
465 | /* Subsequent hashes absorb previous result */ | |
466 | HD(results); | |
467 | } | |
468 | ||
469 | HX((e = gettimeofday(&tv, NULL)) == -1, tv); | |
470 | if (e != -1) { | |
471 | cnt += (int)tv.tv_sec; | |
472 | cnt += (int)tv.tv_usec; | |
473 | } | |
474 | ||
475 | HD(cnt); | |
476 | } | |
477 | #ifdef AT_RANDOM | |
478 | /* Not as random as you think but we take what we are given */ | |
479 | p = (char *) getauxval(AT_RANDOM); | |
480 | if (p) | |
481 | HR(p, 16); | |
482 | #endif | |
483 | #ifdef AT_SYSINFO_EHDR | |
484 | p = (char *) getauxval(AT_SYSINFO_EHDR); | |
485 | if (p) | |
486 | HR(p, pgs); | |
487 | #endif | |
488 | #ifdef AT_BASE | |
489 | p = (char *) getauxval(AT_BASE); | |
490 | if (p) | |
491 | HD(p); | |
492 | #endif | |
493 | ||
494 | SHA512_Final(results, &ctx); | |
495 | memcpy((char*)buf + i, results, min(sizeof(results), len - i)); | |
496 | i += min(sizeof(results), len - i); | |
497 | } | |
498 | memset(results, 0, sizeof results); | |
499 | if (gotdata(buf, len) == 0) { | |
500 | errno = save_errno; | |
501 | return 0; /* satisfied */ | |
502 | } | |
503 | errno = EIO; | |
504 | return -1; | |
505 | } |