[core] rand.[ch] to use better RNGs when available
prefer RAND_pseudo_bytes() (openssl), arc4random() or jrand48(), if available, over rand() These are not necessarily cryptographically secure, but should be better than rand()personal/stbuehler/mod-csrf
parent
b8b38f3067
commit
7f4e156e5f
|
@ -174,6 +174,7 @@ if 1:
|
|||
fcntl.h
|
||||
getopt.h
|
||||
inttypes.h
|
||||
linux/random.h
|
||||
netinet/in.h
|
||||
poll.h
|
||||
pwd.h
|
||||
|
@ -213,7 +214,9 @@ if 1:
|
|||
gethostbyname poll epoll_ctl getrlimit chroot \
|
||||
getuid select signal pathconf madvise prctl\
|
||||
writev sigaction sendfile64 send_file kqueue port_create localtime_r posix_fadvise issetugid inet_pton \
|
||||
memset_s explicit_bzero clock_gettime'))
|
||||
memset_s explicit_bzero clock_gettime \
|
||||
getentropy arc4random jrand48'))
|
||||
checkFunc(autoconf, getrandom, linux/random.h)
|
||||
|
||||
checkTypes(autoconf, Split('pid_t size_t off_t'))
|
||||
|
||||
|
|
|
@ -762,7 +762,11 @@ AC_CHECK_FUNCS([dup2 getcwd inet_ntoa inet_ntop inet_pton issetugid memset mmap
|
|||
gethostbyname poll epoll_ctl getrlimit chroot \
|
||||
getuid select signal pathconf madvise posix_fadvise posix_madvise \
|
||||
writev sigaction sendfile64 send_file kqueue port_create localtime_r gmtime_r \
|
||||
memset_s explicit_bzero clock_gettime])
|
||||
memset_s explicit_bzero clock_gettime \
|
||||
getentropy arc4random jrand48])
|
||||
AC_CHECK_HEADERS([linux/random.h],[
|
||||
AC_CHECK_FUNC([getrandom], AC_DEFINE([HAVE_GETRANDOM], [1], [getrandom]))
|
||||
])
|
||||
|
||||
AC_MSG_CHECKING(if weak symbols are supported)
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
|
|
|
@ -120,16 +120,24 @@ set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
|
|||
check_type_size(socklen_t HAVE_SOCKLEN_T)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
check_include_files(linux/random.h HAVE_LINUX_RANDOM_H)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES linux/random.h)
|
||||
check_function_exists(getrandom HAVE_GETRANDOM)
|
||||
set(CMAKE_EXTRA_INCLUDE_FILES)
|
||||
|
||||
check_type_size(long SIZEOF_LONG)
|
||||
check_type_size(off_t SIZEOF_OFF_T)
|
||||
|
||||
check_function_exists(arc4random HAVE_ARC4RANDOM)
|
||||
check_function_exists(chroot HAVE_CHROOT)
|
||||
check_function_exists(epoll_ctl HAVE_EPOLL_CTL)
|
||||
check_function_exists(fork HAVE_FORK)
|
||||
check_function_exists(getentropy HAVE_GETENTROPY)
|
||||
check_function_exists(getrlimit HAVE_GETRLIMIT)
|
||||
check_function_exists(getuid HAVE_GETUID)
|
||||
check_function_exists(gmtime_r HAVE_GMTIME_R)
|
||||
check_function_exists(inet_ntop HAVE_INET_NTOP)
|
||||
check_function_exists(jrand48 HAVE_JRAND48)
|
||||
check_function_exists(kqueue HAVE_KQUEUE)
|
||||
check_function_exists(localtime_r HAVE_LOCALTIME_R)
|
||||
check_function_exists(lstat HAVE_LSTAT)
|
||||
|
@ -519,6 +527,7 @@ set(COMMON_SRC
|
|||
network_write.c network_linux_sendfile.c
|
||||
network_freebsd_sendfile.c
|
||||
network_solaris_sendfilev.c network_openssl.c
|
||||
rand.c
|
||||
status_counter.c safe_memclear.c network_darwin_sendfile.c
|
||||
)
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ common_src=base64.c buffer.c log.c \
|
|||
network_write_mmap.c network_write_no_mmap.c \
|
||||
network_freebsd_sendfile.c network_writev.c \
|
||||
network_solaris_sendfilev.c network_openssl.c \
|
||||
rand.c \
|
||||
splaytree.c status_counter.c \
|
||||
safe_memclear.c network_darwin_sendfile.c
|
||||
|
||||
|
@ -324,6 +325,7 @@ hdr = server.h base64.h buffer.h network.h log.h keyvalue.h \
|
|||
network_backends.h configfile.h \
|
||||
mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \
|
||||
configparser.h mod_ssi_exprparser.h \
|
||||
rand.h \
|
||||
sys-endian.h sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \
|
||||
safe_memclear.h splaytree.h proc_open.h status_counter.h \
|
||||
mod_magnet_cache.h \
|
||||
|
|
|
@ -60,6 +60,7 @@ common_src = Split("base64.c buffer.c log.c \
|
|||
network_write.c network_linux_sendfile.c \
|
||||
network_freebsd_sendfile.c \
|
||||
network_solaris_sendfilev.c network_openssl.c \
|
||||
rand.c \
|
||||
status_counter.c safe_memclear.c network_darwin_sendfile.c \
|
||||
")
|
||||
|
||||
|
|
|
@ -641,7 +641,6 @@ typedef struct server {
|
|||
time_t startup_ts;
|
||||
|
||||
char entropy[8]; /* from /dev/[u]random if possible, otherwise rand() */
|
||||
char is_real_entropy; /* whether entropy is from /dev/[u]random */
|
||||
|
||||
buffer *ts_debug_str;
|
||||
buffer *ts_date_str;
|
||||
|
|
|
@ -416,6 +416,7 @@ int mod_auth_plugin_init(plugin *p) {
|
|||
#include "response.h"
|
||||
#include "base64.h"
|
||||
#include "md5.h"
|
||||
#include "rand.h"
|
||||
|
||||
static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) {
|
||||
UNUSED(srv);
|
||||
|
@ -769,7 +770,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
|
|||
return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0);
|
||||
}
|
||||
|
||||
/* check age of nonce. Note that rand() is used in nonce generation
|
||||
/* check age of nonce. Note, random data is used in nonce generation
|
||||
* in mod_auth_send_401_unauthorized_digest(). If that were replaced
|
||||
* with nanosecond time, then nonce secret would remain unique enough
|
||||
* for the purposes of Digest auth, and would be reproducible (and
|
||||
|
@ -820,7 +821,7 @@ static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *
|
|||
li_itostrn(hh, sizeof(hh), srv->cur_ts);
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
|
||||
li_itostrn(hh, sizeof(hh), rand());
|
||||
li_itostrn(hh, sizeof(hh), li_rand());
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
|
||||
|
||||
li_MD5_Final(h, &Md5Ctx);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "base.h"
|
||||
#include "log.h"
|
||||
#include "buffer.h"
|
||||
#include "rand.h"
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
|
@ -230,7 +231,7 @@ URIHANDLER_FUNC(mod_usertrack_uri_handler) {
|
|||
li_itostrn(hh, sizeof(hh), srv->cur_ts);
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy));
|
||||
li_itostrn(hh, sizeof(hh), rand());
|
||||
li_itostrn(hh, sizeof(hh), li_rand());
|
||||
li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh));
|
||||
|
||||
li_MD5_Final(h, &Md5Ctx);
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
#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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/rand.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.
|
||||
*
|
||||
* Note: results from li_rand() are not necessarily cryptographically random.
|
||||
*
|
||||
* 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, RNDGETENTCNT, &entropy) && entropy >= num*8)
|
||||
#endif
|
||||
rd = read(fd, buf, (size_t)num);
|
||||
close(fd);
|
||||
if (rd == num) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned short xsubi[3];
|
||||
|
||||
void li_rand_reseed (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;
|
||||
if (1 == li_rand_device_bytes((unsigned char *)xsubi, (int)sizeof(xsubi))) {
|
||||
u = ((unsigned int)xsubi[0] << 16) | xsubi[1];
|
||||
srand(u); /*(initialize just in case rand() used elsewhere)*/
|
||||
}
|
||||
else {
|
||||
#ifdef HAVE_ARC4RANDOM
|
||||
srand(arc4random()); /*(initialize just in case rand() used elsewhere)*/
|
||||
arc4random_buf(xsubi, sizeof(xsubi));
|
||||
#else
|
||||
/* NOTE: not cryptographically random !!! */
|
||||
srand((unsigned int)(time(NULL) ^ getpid()));
|
||||
for (u = 0; u < sizeof(unsigned short); ++u)
|
||||
xsubi[u] = (unsigned short)(rand() & 0xFFFF);
|
||||
#endif
|
||||
}
|
||||
#ifdef USE_OPENSSL
|
||||
RAND_poll();
|
||||
RAND_seed(xsubi, (int)sizeof(xsubi));
|
||||
#endif
|
||||
}
|
||||
|
||||
int li_rand (void)
|
||||
{
|
||||
/* randomness *is not* cryptographically strong */
|
||||
/* (attempt to use better mechanisms to replace the more portable rand()) */
|
||||
#ifdef USE_OPENSSL
|
||||
int i;
|
||||
if (-1 != RAND_pseudo_bytes((unsigned char *)&i, sizeof(i))) return i;
|
||||
#endif
|
||||
#ifdef HAVE_ARC4RANDOM
|
||||
return (int)arc4random();
|
||||
#endif
|
||||
#ifdef HAVE_JRAND48
|
||||
/*(FYI: jrand48() reentrant, but use of file-scoped static xsubi[] is not)*/
|
||||
return (int)jrand48(xsubi);
|
||||
#else
|
||||
return rand();
|
||||
#endif
|
||||
}
|
||||
|
||||
int li_rand_bytes (unsigned char *buf, int num)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
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 !!! */
|
||||
for (int i = 0; i < num; ++i)
|
||||
buf[i] = li_rand() & 0xFF;
|
||||
/*(openssl RAND_pseudo_bytes rc for non-cryptographically random data)*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void li_rand_cleanup (void)
|
||||
{
|
||||
#ifdef USE_OPENSSL
|
||||
RAND_cleanup();
|
||||
#endif
|
||||
safe_memclear(xsubi, sizeof(xsubi));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef LI_RAND_H_
|
||||
#define LI_RAND_H_
|
||||
#include "first.h"
|
||||
|
||||
int li_rand (void);
|
||||
void li_rand_reseed (void);
|
||||
int li_rand_bytes (unsigned char *buf, int num);
|
||||
void li_rand_cleanup (void);
|
||||
|
||||
#endif
|
22
src/server.c
22
src/server.c
|
@ -5,6 +5,7 @@
|
|||
#include "network.h"
|
||||
#include "log.h"
|
||||
#include "keyvalue.h"
|
||||
#include "rand.h"
|
||||
#include "response.h"
|
||||
#include "request.h"
|
||||
#include "chunk.h"
|
||||
|
@ -202,8 +203,6 @@ static int daemonize(void) {
|
|||
|
||||
static server *server_init(void) {
|
||||
int i;
|
||||
FILE *frandom = NULL;
|
||||
|
||||
server *srv = calloc(1, sizeof(*srv));
|
||||
force_assert(srv);
|
||||
#define CLEAN(x) \
|
||||
|
@ -244,20 +243,8 @@ static server *server_init(void) {
|
|||
srv->mtime_cache[i].str = buffer_init();
|
||||
}
|
||||
|
||||
if ((NULL != (frandom = fopen("/dev/urandom", "rb")) || NULL != (frandom = fopen("/dev/random", "rb")))
|
||||
&& 1 == fread(srv->entropy, sizeof(srv->entropy), 1, frandom)) {
|
||||
unsigned int e;
|
||||
memcpy(&e, srv->entropy, sizeof(e) < sizeof(srv->entropy) ? sizeof(e) : sizeof(srv->entropy));
|
||||
srand(e);
|
||||
srv->is_real_entropy = 1;
|
||||
} else {
|
||||
unsigned int j;
|
||||
srand(time(NULL) ^ getpid());
|
||||
srv->is_real_entropy = 0;
|
||||
for (j = 0; j < sizeof(srv->entropy); j++)
|
||||
srv->entropy[j] = rand();
|
||||
}
|
||||
if (frandom) fclose(frandom);
|
||||
li_rand_reseed();
|
||||
li_rand_bytes((unsigned char *)srv->entropy, (int)sizeof(srv->entropy));
|
||||
|
||||
srv->cur_ts = time(NULL);
|
||||
srv->startup_ts = srv->cur_ts;
|
||||
|
@ -404,6 +391,7 @@ static void server_free(server *srv) {
|
|||
EVP_cleanup();
|
||||
}
|
||||
#endif
|
||||
li_rand_cleanup();
|
||||
|
||||
free(srv);
|
||||
}
|
||||
|
@ -1441,6 +1429,8 @@ int main (int argc, char **argv) {
|
|||
pid_fd = -1;
|
||||
}
|
||||
buffer_reset(srv->srvconf.pid_file);
|
||||
|
||||
li_rand_reseed();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue