lighttpd 1.4.x
https://www.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
9.4 KiB
295 lines
9.4 KiB
#include "first.h" |
|
|
|
#include "rand.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" /* USE_LIB_CRYPTO */ |
|
#ifdef USE_NETTLE_CRYPTO |
|
#undef USE_OPENSSL_CRYPTO |
|
#include <nettle/knuth-lfib.h> |
|
#include <nettle/arcfour.h> |
|
#include <nettle/yarrow.h> |
|
#endif |
|
#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], 0, 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]; |
|
#ifdef USE_NETTLE_CRYPTO |
|
static struct knuth_lfib_ctx knuth_lfib_ctx; |
|
static struct arcfour_ctx arcfour_ctx; |
|
static struct yarrow256_ctx yarrow256_ctx; |
|
#endif |
|
|
|
#ifdef USE_NETTLE_CRYPTO |
|
/* adapted from Nettle documentation arcfour_set_key_hashed() in nettle.pdf */ |
|
/* A more robust key setup function for ARCFOUR */ |
|
static void |
|
li_arcfour_init_random_key_hashed(struct arcfour_ctx *ctx) |
|
{ |
|
uint8_t key[ARCFOUR_KEY_SIZE]; |
|
const size_t length = sizeof(key); |
|
if (1 != li_rand_device_bytes(key, (int)sizeof(key))) { |
|
log_failed_assert(__FILE__, __LINE__, |
|
"gathering entropy for arcfour seed failed"); |
|
} |
|
memset(ctx, 0, sizeof(*ctx)); |
|
|
|
struct sha256_ctx hash; |
|
uint8_t digest[SHA256_DIGEST_SIZE]; |
|
uint8_t buf[0x200]; |
|
memset(buf, 0, sizeof(buf)); |
|
sha256_init(&hash); |
|
sha256_update(&hash, length, key); |
|
sha256_digest(&hash, SHA256_DIGEST_SIZE, digest); |
|
nettle_arcfour_set_key(ctx, SHA256_DIGEST_SIZE, digest); |
|
nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf); |
|
nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf); |
|
nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf); |
|
} |
|
#endif |
|
|
|
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_NETTLE_CRYPTO |
|
nettle_knuth_lfib_init(&knuth_lfib_ctx, u); |
|
nettle_yarrow256_init(&yarrow256_ctx, 0, NULL); |
|
li_arcfour_init_random_key_hashed(&arcfour_ctx); |
|
#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 USE_NETTLE_CRYPTO |
|
int i = (int)nettle_knuth_lfib_get(&knuth_lfib_ctx); |
|
nettle_arcfour_crypt(&arcfour_ctx, sizeof(i), (uint8_t *)&i, (uint8_t *)&i); |
|
if (i) return i; /*(cond to avoid compiler warning for code after return)*/ |
|
#endif |
|
#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_NETTLE_CRYPTO |
|
#if 0 /* not implemented: periodic nettle_yarrow256_update() and reseed */ |
|
if (!nettle_yarrow256_is_seeded(&yarrow256_ctx)) { |
|
uint8_t seed_file[YARROW256_SEED_FILE_SIZE]; |
|
if (1 == li_rand_device_bytes((unsigned char *)seed_file, |
|
(int)sizeof(seed_file))) { |
|
nettle_yarrow256_seed(&yarrow256_ctx, sizeof(seed_file), seed_file); |
|
} |
|
} |
|
if (nettle_yarrow256_is_seeded(&yarrow256_ctx)) { |
|
nettle_yarrow256_random(&yarrow256_ctx, (size_t)num, (uint8_t *)buf); |
|
return 1; |
|
} |
|
#endif |
|
#endif |
|
#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)); |
|
}
|
|
|