diff --git a/arc4random.c b/arc4random.c index 28f0cdbf..a039711f 100644 --- a/arc4random.c +++ b/arc4random.c @@ -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 #include #include +#include #endif #include #include @@ -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)) + 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]] + ) +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)