Skip to content

Commit

Permalink
Seed the RNG using sysctl() as well as /dev/urandom
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nmathewson committed Apr 23, 2010
1 parent b1c7950 commit 71fc3eb
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 17 deletions.
144 changes: 128 additions & 16 deletions arc4random.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -135,29 +137,20 @@ read_all(int fd, unsigned char *buf, size_t count)
}
#endif

/* This is adapted from Tor's crypto_seed_rng() */
#ifdef WIN32
#define TRY_SEED_WIN32
static int
arc4_seed(void)
arc4_seed_win32(void)
{
unsigned char buf[ADD_ENTROPY];

/* local variables */
#ifdef WIN32
/* 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;
Expand All @@ -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)
Expand All @@ -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
Expand Down
8 changes: 7 additions & 1 deletion configure.in
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 71fc3eb

Please sign in to comment.