]> granicus.if.org Git - libevent/commitdiff
Seed the RNG using sysctl() as well as /dev/urandom
authorNick Mathewson <nickm@torproject.org>
Thu, 4 Mar 2010 06:13:51 +0000 (01:13 -0500)
committerNick Mathewson <nickm@torproject.org>
Fri, 23 Apr 2010 19:46:04 +0000 (15:46 -0400)
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.

arc4random.c
configure.in

index 28f0cdbf54586afb0a1c14ab269eaac314c6e6b9..a039711ff954c90fb0a578666abe2e2d835f49a0 100644 (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() */
+#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;
@@ -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
index 861997b446fd45e3dd860a3041207efbaf0ce4aa..171801fecdc7da9a58bce1a1d4bca251f8ae74e6 100644 (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)