[doc] remove reference to Linux rt-signals
[lighttpd.git] / src / rand.c
blobac79341936dd28d1f7b1308f2cbbc2c3c138e8cc
1 #include "first.h"
3 #include "rand.h"
4 #include "base.h"
5 #include "fdevent.h"
6 #include "safe_memclear.h"
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 #include <unistd.h>
18 #ifdef USE_OPENSSL
19 #include <openssl/rand.h>
20 #endif
21 #ifdef HAVE_LINUX_RANDOM_H
22 #include <sys/syscall.h>
23 #include <linux/random.h>
24 #endif
25 #ifdef RNDGETENTCNT
26 #include <sys/ioctl.h>
27 #endif
29 /* Take some reasonable steps to attempt to *seed* random number generators with
30 * cryptographically random data. Some of these initialization routines may
31 * block, and are intended to be called only at startup in lighttpd, or
32 * immediately after fork() to start lighttpd workers.
34 * Note: results from li_rand() are not necessarily cryptographically random.
36 * https://wiki.openssl.org/index.php/Random_Numbers
37 * https://wiki.openssl.org/index.php/Random_fork-safety
39 * openssl random number generators are not thread-safe by default
40 * https://wiki.openssl.org/index.php/Manual:Threads(3)
42 * RFE: add more paranoid checks from the following to improve confidence:
43 * http://insanecoding.blogspot.co.uk/2014/05/a-good-idea-with-bad-usage-devurandom.html
44 * RFE: retry on EINTR
45 * RFE: check RAND_status()
48 static int li_getentropy (void *buf, size_t buflen)
50 #ifdef HAVE_GETENTROPY
51 return getentropy(buf, buflen);
52 #else
53 /*(see NOTES section in 'man getrandom' on Linux)*/
54 #if defined(HAVE_GETRANDOM) || defined(SYS_getrandom)
55 if (buflen <= 256) {
56 #ifdef HAVE_GETRANDOM /*(not implemented in glibc yet)*/
57 int num = getrandom(buf, buflen, 0);
58 #elif defined(SYS_getrandom)
59 /* https://lwn.net/Articles/605828/ */
60 /* https://bbs.archlinux.org/viewtopic.php?id=200039 */
61 int num = (int)syscall(SYS_getrandom, buf, buflen, 0);
62 #endif
63 if (num == (int)buflen) return 0;
64 if (num < 0) return num; /* -1 */
66 #else
67 UNUSED(buf);
68 UNUSED(buflen);
69 #endif
70 errno = EIO;
71 return -1;
72 #endif
75 static int li_rand_device_bytes (unsigned char *buf, int num)
77 /* randomness from these devices is cryptographically strong,
78 * unless /dev/urandom is low on entropy */
80 static const char * const devices[] = {
81 #ifdef __OpenBSD__
82 "/dev/arandom",
83 #endif
84 "/dev/urandom",
85 "/dev/random"
88 /* device files might not be available in chroot environment,
89 * so prefer syscall, if available */
90 if (0 == li_getentropy(buf, (size_t)num)) return 1;
92 for (unsigned int u = 0; u < sizeof(devices)/sizeof(devices[0]); ++u) {
93 /*(some systems might have symlink to another device; omit O_NOFOLLOW)*/
94 int fd = fdevent_open_cloexec(devices[u], O_RDONLY, 0);
95 if (fd >= 0) {
96 ssize_t rd = 0;
97 #ifdef RNDGETENTCNT
98 int entropy;
99 if (0 == ioctl(fd, RNDGETENTCNT, &entropy) && entropy >= num*8)
100 #endif
101 rd = read(fd, buf, (size_t)num);
102 close(fd);
103 if (rd == num) {
104 return 1;
109 return 0;
112 static unsigned short xsubi[3];
114 void li_rand_reseed (void)
116 /* (intended to be called at init and after fork() in order to re-seed PRNG
117 * so that forked children, grandchildren, etc do not share PRNG seed)
118 * https://github.com/ramsey/uuid/issues/80
119 * https://www.agwa.name/blog/post/libressls_prng_is_unsafe_on_linux
120 * (issue in early version of libressl has since been fixed)
121 * https://github.com/libressl-portable/portable/commit/32d9eeeecf4e951e1566d5f4a42b36ea37b60f35
123 unsigned int u;
124 if (1 == li_rand_device_bytes((unsigned char *)xsubi, (int)sizeof(xsubi))) {
125 u = ((unsigned int)xsubi[0] << 16) | xsubi[1];
127 else {
128 #ifdef HAVE_ARC4RANDOM_BUF
129 u = arc4random();
130 arc4random_buf(xsubi, sizeof(xsubi));
131 #else
132 /* NOTE: not cryptographically random !!! */
133 srand((unsigned int)(time(NULL) ^ getpid()));
134 for (u = 0; u < sizeof(unsigned short); ++u)
135 /* coverity[dont_call : FALSE] */
136 xsubi[u] = (unsigned short)(rand() & 0xFFFF);
137 u = ((unsigned int)xsubi[0] << 16) | xsubi[1];
138 #endif
140 srand(u); /*(initialize just in case rand() used elsewhere)*/
141 #ifdef HAVE_SRANDOM
142 srandom(u); /*(initialize just in case random() used elsewhere)*/
143 #endif
144 #ifdef USE_OPENSSL
145 RAND_poll();
146 RAND_seed(xsubi, (int)sizeof(xsubi));
147 #endif
150 int li_rand (void)
152 /* randomness *is not* cryptographically strong */
153 /* (attempt to use better mechanisms to replace the more portable rand()) */
154 #ifdef USE_OPENSSL /* (RAND_pseudo_bytes() is deprecated in openssl 1.1.0) */
155 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
156 int i;
157 if (-1 != RAND_pseudo_bytes((unsigned char *)&i, sizeof(i))) return i;
158 #endif
159 #endif
160 #ifdef HAVE_ARC4RANDOM_BUF
161 return (int)arc4random();
162 #elif defined(HAVE_SRANDOM)
163 /* coverity[dont_call : FALSE] */
164 return (int)random();
165 #elif defined(HAVE_JRAND48)
166 /*(FYI: jrand48() reentrant, but use of file-scoped static xsubi[] is not)*/
167 /* coverity[dont_call : FALSE] */
168 return (int)jrand48(xsubi);
169 #else
170 /* coverity[dont_call : FALSE] */
171 return rand();
172 #endif
175 int li_rand_bytes (unsigned char *buf, int num)
177 #ifdef USE_OPENSSL
178 int rc = RAND_bytes(buf, num);
179 if (-1 != rc) {
180 return rc;
182 #endif
183 if (1 == li_rand_device_bytes(buf, num)) {
184 return 1;
186 else {
187 /* NOTE: not cryptographically random !!! */
188 for (int i = 0; i < num; ++i)
189 buf[i] = li_rand() & 0xFF;
190 /*(openssl RAND_pseudo_bytes rc for non-cryptographically random data)*/
191 return 0;
195 void li_rand_cleanup (void)
197 #ifdef USE_OPENSSL
198 RAND_cleanup();
199 #endif
200 safe_memclear(xsubi, sizeof(xsubi));