diff options
Diffstat (limited to 'data/lighttpd/lighttpd-1.4.53/src/rand.c')
-rw-r--r-- | data/lighttpd/lighttpd-1.4.53/src/rand.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/data/lighttpd/lighttpd-1.4.53/src/rand.c b/data/lighttpd/lighttpd-1.4.53/src/rand.c new file mode 100644 index 000000000..af24d46d7 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/rand.c @@ -0,0 +1,232 @@ +#include "first.h" + +#include "rand.h" +#include "base.h" +#include "fdevent.h" +#include "safe_memclear.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */ +#include <openssl/rand.h> +#endif +#ifdef HAVE_GETENTROPY +#include <sys/random.h> +#endif +#ifdef HAVE_LINUX_RANDOM_H +#include <sys/syscall.h> +#include <linux/random.h> +#endif +#ifdef RNDGETENTCNT +#include <sys/ioctl.h> +#endif + +/* Take some reasonable steps to attempt to *seed* random number generators with + * cryptographically random data. Some of these initialization routines may + * block, and are intended to be called only at startup in lighttpd, or + * immediately after fork() to start lighttpd workers. + * + * Update: li_rand_init() is now deferred until first use so that installations + * that do not use modules which use these routines do need to potentially block + * at startup. Current use by core lighttpd modules is in mod_auth HTTP Digest + * auth and in mod_usertrack. Deferring collection of random data until first + * use may allow sufficient entropy to be collected by kernel before first use, + * helping reduce or avoid situations in low-entropy-generating embedded devices + * which might otherwise block lighttpd for minutes at device startup. + * Further discussion in https://redmine.lighttpd.net/boards/2/topics/6981 + * + * Note: results from li_rand_pseudo_bytes() are not necessarily + * cryptographically random and must not be used for purposes such + * as key generation which require cryptographic randomness. + * + * https://wiki.openssl.org/index.php/Random_Numbers + * https://wiki.openssl.org/index.php/Random_fork-safety + * + * openssl random number generators are not thread-safe by default + * https://wiki.openssl.org/index.php/Manual:Threads(3) + * + * RFE: add more paranoid checks from the following to improve confidence: + * http://insanecoding.blogspot.co.uk/2014/05/a-good-idea-with-bad-usage-devurandom.html + * RFE: retry on EINTR + * RFE: check RAND_status() + */ + +static int li_getentropy (void *buf, size_t buflen) +{ + #ifdef HAVE_GETENTROPY + return getentropy(buf, buflen); + #else + /*(see NOTES section in 'man getrandom' on Linux)*/ + #if defined(HAVE_GETRANDOM) || defined(SYS_getrandom) + if (buflen <= 256) { + #ifdef HAVE_GETRANDOM /*(not implemented in glibc yet)*/ + int num = getrandom(buf, buflen, 0); + #elif defined(SYS_getrandom) + /* https://lwn.net/Articles/605828/ */ + /* https://bbs.archlinux.org/viewtopic.php?id=200039 */ + int num = (int)syscall(SYS_getrandom, buf, buflen, 0); + #endif + if (num == (int)buflen) return 0; + if (num < 0) return num; /* -1 */ + } + #else + UNUSED(buf); + UNUSED(buflen); + #endif + errno = EIO; + return -1; + #endif +} + +static int li_rand_device_bytes (unsigned char *buf, int num) +{ + /* randomness from these devices is cryptographically strong, + * unless /dev/urandom is low on entropy */ + + static const char * const devices[] = { + #ifdef __OpenBSD__ + "/dev/arandom", + #endif + "/dev/urandom", + "/dev/random" + }; + + /* device files might not be available in chroot environment, + * so prefer syscall, if available */ + if (0 == li_getentropy(buf, (size_t)num)) return 1; + + for (unsigned int u = 0; u < sizeof(devices)/sizeof(devices[0]); ++u) { + /*(some systems might have symlink to another device; omit O_NOFOLLOW)*/ + int fd = fdevent_open_cloexec(devices[u], O_RDONLY, 0); + if (fd >= 0) { + ssize_t rd = 0; + #ifdef RNDGETENTCNT + int entropy; + if (0 == ioctl(fd, (unsigned long)(RNDGETENTCNT), &entropy) + && entropy >= num*8) + #endif + rd = read(fd, buf, (size_t)num); + close(fd); + if (rd == num) { + return 1; + } + } + } + + return 0; +} + +static int li_rand_inited; +static unsigned short xsubi[3]; + +static void li_rand_init (void) +{ + /* (intended to be called at init and after fork() in order to re-seed PRNG + * so that forked children, grandchildren, etc do not share PRNG seed) + * https://github.com/ramsey/uuid/issues/80 + * https://www.agwa.name/blog/post/libressls_prng_is_unsafe_on_linux + * (issue in early version of libressl has since been fixed) + * https://github.com/libressl-portable/portable/commit/32d9eeeecf4e951e1566d5f4a42b36ea37b60f35 + */ + unsigned int u; + li_rand_inited = 1; + if (1 == li_rand_device_bytes((unsigned char *)xsubi, (int)sizeof(xsubi))) { + u = ((unsigned int)xsubi[0] << 16) | xsubi[1]; + } + else { + #ifdef HAVE_ARC4RANDOM_BUF + u = arc4random(); + arc4random_buf(xsubi, sizeof(xsubi)); + #else + /* NOTE: not cryptographically random !!! */ + srand((unsigned int)(time(NULL) ^ getpid())); + for (u = 0; u < sizeof(unsigned short); ++u) + /* coverity[dont_call : FALSE] */ + xsubi[u] = (unsigned short)(rand() & 0xFFFF); + u = ((unsigned int)xsubi[0] << 16) | xsubi[1]; + #endif + } + srand(u); /*(initialize just in case rand() used elsewhere)*/ + #ifdef HAVE_SRANDOM + srandom(u); /*(initialize just in case random() used elsewhere)*/ + #endif + #ifdef USE_OPENSSL_CRYPTO + RAND_poll(); + RAND_seed(xsubi, (int)sizeof(xsubi)); + #endif +} + +void li_rand_reseed (void) +{ + if (li_rand_inited) li_rand_init(); +} + +int li_rand_pseudo (void) +{ + /* randomness *is not* cryptographically strong */ + /* (attempt to use better mechanisms to replace the more portable rand()) */ + #ifdef USE_OPENSSL_CRYPTO /* (openssl 1.1.0 deprecates RAND_pseudo_bytes()) */ + #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + int i; + if (-1 != RAND_pseudo_bytes((unsigned char *)&i, sizeof(i))) return i; + #endif + #endif + if (!li_rand_inited) li_rand_init(); + #ifdef HAVE_ARC4RANDOM_BUF + return (int)arc4random(); + #elif defined(HAVE_SRANDOM) + /* coverity[dont_call : FALSE] */ + return (int)random(); + #elif defined(HAVE_JRAND48) + /*(FYI: jrand48() reentrant, but use of file-scoped static xsubi[] is not)*/ + /* coverity[dont_call : FALSE] */ + return (int)jrand48(xsubi); + #else + /* coverity[dont_call : FALSE] */ + return rand(); + #endif +} + +void li_rand_pseudo_bytes (unsigned char *buf, int num) +{ + for (int i = 0; i < num; ++i) + buf[i] = li_rand_pseudo() & 0xFF; +} + +int li_rand_bytes (unsigned char *buf, int num) +{ + #ifdef USE_OPENSSL_CRYPTO + int rc = RAND_bytes(buf, num); + if (-1 != rc) { + return rc; + } + #endif + if (1 == li_rand_device_bytes(buf, num)) { + return 1; + } + else { + /* NOTE: not cryptographically random !!! */ + li_rand_pseudo_bytes(buf, num); + /*(openssl RAND_pseudo_bytes rc for non-cryptographically random data)*/ + return 0; + } +} + +void li_rand_cleanup (void) +{ + #ifdef USE_OPENSSL_CRYPTO + #if OPENSSL_VERSION_NUMBER < 0x10100000L + RAND_cleanup(); + #endif + #endif + safe_memclear(xsubi, sizeof(xsubi)); +} |