Seed the RNG using sysctl() as well as /dev/urandom

William Ahern points out that if the user has chrooted, they might not
have a working /dev/urandom.  Linux and many of the BSDs, however,
define a sysctl interface to their kernel random number generators.

This patch takes a belt-and-suspenders approach and tries to do use the
sysctl _and_ the /dev/urandom approach if both are present.  When using
the sysctl approach, it tries to bulletproof itself by checking to make
sure that the buffers are actually set by the sysctl calls.
This commit is contained in:
Nick Mathewson 2010-03-04 01:13:51 -05:00
parent b1c795007f
commit 71fc3eb08b
2 changed files with 136 additions and 18 deletions

View File

@ -1,5 +1,6 @@
/* Portable arc4random.c based on arc4random.c from OpenBSD.
* Portable version by Chris Davis, adapted for Libevent by Nick Mathewson
* Copyright (c) 2010 Chris Davis, Niels Provos, and Nick Mathewson
*
* Note that in Libevent, this file isn't compiled directly. Instead,
* it's included from evutil_rand.c
@ -56,6 +57,7 @@
#include <unistd.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#endif
#include <limits.h>
#include <stdlib.h>
@ -135,29 +137,20 @@ read_all(int fd, unsigned char *buf, size_t count)
}
#endif
/* This is adapted from Tor's crypto_seed_rng() */
static int
arc4_seed(void)
{
unsigned char buf[ADD_ENTROPY];
/* local variables */
#ifdef WIN32
#define TRY_SEED_WIN32
static int
arc4_seed_win32(void)
{
/* This is adapted from Tor's crypto_seed_rng() */
static int provider_set = 0;
static HCRYPTPROV provider;
#else
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
int fd, i;
size_t n;
#endif
unsigned char buf[ADD_ENTROPY];
#ifdef WIN32
if (!provider_set) {
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET)
if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
return -1;
}
provider_set = 1;
@ -168,7 +161,100 @@ arc4_seed(void)
memset(buf, 0, sizeof(buf));
arc4_seeded_ok = 1;
return 0;
#else
}
#endif
#if defined(_EVENT_HAVE_SYS_SYSCTL_H)
#if _EVENT_HAVE_DECL_CTL_KERN && _EVENT_HAVE_DECL_KERN_RANDOM && _EVENT_HAVE_DECL_RANDOM_UUID
#define TRY_SEED_SYSCTL_LINUX
static int
arc4_seed_sysctl_linux(void)
{
/* Based on code by William Ahern, this function tries to use the
* RANDOM_UUID sysctl to get entropy from the kernel. This can work
* even if /dev/urandom is inaccessible for some reason (e.g., we're
* running in a chroot). */
int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID };
unsigned char buf[ADD_ENTROPY];
size_t len, n;
int i, any_set;
memset(buf, 0, sizeof(buf));
for (len = 0; len < sizeof(buf); len += n) {
n = sizeof(buf) - len;
if (0 != sysctl(mib, 3, &buf[len], &n, NULL, 0))
return -1;
}
/* make sure that the buffer actually got set. */
for (i=any_set=0; i<sizeof(buf); ++i) {
any_set |= buf[i];
}
if (!any_set)
return -1;
arc4_addrandom(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
arc4_seeded_ok = 1;
return 0;
}
#endif
#if _EVENT_HAVE_DECL_CTL_KERN && _EVENT_HAVE_DECL_KERN_ARAND
#define TRY_SEED_SYSCTL_BSD
static int
arc4_seed_sysctl_bsd(void)
{
/* Based on code from William Ahern and from OpenBSD, this function
* tries to use the KERN_ARAND syscall to get entropy from the kernel.
* This can work even if /dev/urandom is inaccessible for some reason
* (e.g., we're running in a chroot). */
int mib[] = { CTL_KERN, KERN_ARAND };
unsigned char buf[ADD_ENTROPY];
size_t len, n;
int i, any_set;
memset(buf, 0, sizeof(buf));
len = sizeof(buf);
if (sysctl(mib, 2, rnd, &len, NULL, 0) == -1) {
for (len = 0; len < sizeof(buf); len += sizeof(unsigned)) {
n = sizeof(unsigned);
if (n + len > sizeof(buf))
n = len - sizeof(buf);
if (sysctl(mib, 2, &buf[len], &n, NULL, 0) == -1)
return -1;
}
}
/* make sure that the buffer actually got set. */
for (i=any_set=0; i<sizeof(buf); ++i) {
any_set |= buf[i];
}
if (!any_set)
return -1;
arc4_addrandom(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
arc4_seeded_ok = 1;
return 0;
}
#endif
#endif /* defined(_EVENT_HAVE_SYS_SYSCTL_H) */
#ifndef WIN32
#define TRY_SEED_URANDOM
static int
arc4_seed_urandom(void)
{
/* This is adapted from Tor's crypto_seed_rng() */
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
unsigned char buf[ADD_ENTROPY];
int fd, i;
size_t n;
for (i = 0; filenames[i]; ++i) {
fd = open(filenames[i], O_RDONLY, 0);
if (fd<0)
@ -184,7 +270,33 @@ arc4_seed(void)
}
return -1;
}
#endif
static int
arc4_seed(void)
{
int ok = 0;
/* We try every method that might work, and don't give up even if one
* does seem to work. There's no real harm in over-seeding, and if
* one of these sources turns out to be broken, that would be bad. */
#ifdef TRY_SEED_WIN32
if (0 == arc4_seed_win32())
ok = 1;
#endif
#ifdef TRY_SEED_SYSCTL_LINUX
if (0 == arc4_seed_sysctl_linux())
ok = 1;
#endif
#ifdef TRY_SEED_SYSCTL_BSD
if (0 == arc4_seed_sysctl_bsd())
ok = 1;
#endif
#ifdef TRY_SEED_URANDOM
if (0 == arc4_seed_urandom())
ok = 1;
#endif
return ok ? 0 : -1;
}
static void

View File

@ -86,7 +86,7 @@ AC_SUBST(OPENSSL_LIBS)
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h netdb.h)
AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h stddef.h poll.h unistd.h sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h sys/devpoll.h port.h netinet/in.h netinet/in6.h sys/socket.h sys/uio.h arpa/inet.h sys/eventfd.h sys/mman.h sys/sendfile.h netdb.h sys/sysctl.h)
if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
AC_EGREP_CPP(yes,
@ -158,6 +158,12 @@ if test "x$ac_cv_header_sys_time_h" = "xyes"; then
)
fi
if test "x$ac_cv_header_sys_sysctl_h" = "xyes"; then
AC_CHECK_DECLS([CTL_KERN, KERN_RANDOM, RANDOM_UUID, KERN_ARAND], [], [],
[[#include <sys/sysctl.h>]]
)
fi
dnl - check if the macro WIN32 is defined on this compiler.
dnl - (this is how we check for a windows version of GCC)
AC_MSG_CHECKING(for WIN32)