Merge branch 'arc4random'

This commit is contained in:
Nick Mathewson 2010-02-17 23:02:28 -05:00
commit ca46d25b01
10 changed files with 540 additions and 161 deletions

21
LICENSE
View File

@ -45,3 +45,24 @@ win32select.c:
evport.c: evport.c:
Copyright (c) 2007 Sun Microsystems Copyright (c) 2007 Sun Microsystems
==============================
The arc4module is available under the following, sometimes called the
"OpenBSD" license:
Copyright (c) 1996, David Mazieres <dm@uun.org>
Copyright (c) 2008, Damien Miller <djm@openbsd.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -44,7 +44,7 @@ EXTRA_DIST = \
Doxyfile \ Doxyfile \
kqueue.c epoll_sub.c epoll.c select.c poll.c signal.c \ kqueue.c epoll_sub.c epoll.c select.c poll.c signal.c \
evport.c devpoll.c win32select.c event_rpcgen.py \ evport.c devpoll.c win32select.c event_rpcgen.py \
event_iocp.c buffer_iocp.c iocp-internal.h \ event_iocp.c buffer_iocp.c iocp-internal.h arc4random.c \
sample/Makefile.am sample/Makefile.in sample/event-test.c \ sample/Makefile.am sample/Makefile.in sample/event-test.c \
sample/signal-test.c sample/time-test.c \ sample/signal-test.c sample/time-test.c \
test/Makefile.am test/Makefile.in test/bench.c test/regress.c \ test/Makefile.am test/Makefile.in test/bench.c test/regress.c \
@ -107,7 +107,7 @@ event-config.h: config.h
CORE_SRC = event.c evthread.c buffer.c \ CORE_SRC = event.c evthread.c buffer.c \
bufferevent.c bufferevent_sock.c bufferevent_filter.c \ bufferevent.c bufferevent_sock.c bufferevent_filter.c \
bufferevent_pair.c listener.c bufferevent_ratelim.c \ bufferevent_pair.c listener.c bufferevent_ratelim.c \
evmap.c log.c evutil.c strlcpy.c $(SYS_SRC) evmap.c log.c evutil.c evutil_rand.c strlcpy.c $(SYS_SRC)
EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c EXTRA_SRC = event_tagging.c http.c evdns.c evrpc.c

362
arc4random.c Normal file
View File

@ -0,0 +1,362 @@
/* Portable arc4random.c based on arc4random.c from OpenBSD.
* Portable version by Chris Davis, adapted for Libevent by Nick Mathewson
*
* Note that in Libevent, this file isn't compiled directly. Instead,
* it's included from evutil_rand.c
*/
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Arc4 random number generator for OpenBSD.
*
* This code is derived from section 17.1 of Applied Cryptography,
* second edition, which describes a stream cipher allegedly
* compatible with RSA Labs "RC4" cipher (the actual description of
* which is a trade secret). The same algorithm is used as a stream
* cipher called "arcfour" in Tatu Ylonen's ssh package.
*
* Here the stream cipher has been modified always to include the time
* when initializing the state. That makes it impossible to
* regenerate the same random sequence twice, so this can't be used
* for encryption, but will generate good random numbers.
*
* RC4 is a registered trademark of RSA Laboratories.
*/
#ifndef ARC4RANDOM_EXPORT
#define ARC4RANDOM_EXPORT
#endif
#ifndef ARC4RANDOM_UINT32
#define ARC4RANDOM_UINT32 uint32_t
#endif
#ifndef ARC4RANDOM_NO_INCLUDES
#ifdef WIN32
#include <wincrypt.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/time.h>
#endif
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#endif
/* Add platform entropy 32 bytes (256 bits) at a time. */
#define ADD_ENTROPY 32
/* Re-seed from the platform RNG after generating this many bytes. */
#define BYTES_BEFORE_RESEED 1600000
struct arc4_stream {
unsigned char i;
unsigned char j;
unsigned char s[256];
};
static int rs_initialized;
static struct arc4_stream rs;
static pid_t arc4_stir_pid;
static int arc4_count;
static int arc4_seeded_ok;
static inline unsigned char arc4_getbyte(void);
static inline void
arc4_init(void)
{
int n;
for (n = 0; n < 256; n++)
rs.s[n] = n;
rs.i = 0;
rs.j = 0;
}
static inline void
arc4_addrandom(const unsigned char *dat, int datlen)
{
int n;
unsigned char si;
rs.i--;
for (n = 0; n < 256; n++) {
rs.i = (rs.i + 1);
si = rs.s[rs.i];
rs.j = (rs.j + si + dat[n % datlen]);
rs.s[rs.i] = rs.s[rs.j];
rs.s[rs.j] = si;
}
rs.j = rs.i;
}
#ifndef WIN32
static ssize_t
read_all(int fd, unsigned char *buf, size_t count)
{
size_t numread = 0;
ssize_t result;
while (numread < count) {
result = read(fd, buf+numread, count-numread);
if (result<0)
return -1;
else if (result == 0)
break;
numread += result;
}
return (ssize_t)numread;
}
#endif
/* This is adapted from Tor's crypto_seed_rng() */
static int
arc4_seed(void)
{
unsigned char buf[ADD_ENTROPY];
/* local variables */
#ifdef WIN32
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
#ifdef WIN32
if (!provider_set) {
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET)
return -1;
}
provider_set = 1;
}
if (!CryptGenRandom(provider, sizeof(buf), buf))
return -1;
arc4_addrandom(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
arc4_seeded_ok = 1;
return 0;
#else
for (i = 0; filenames[i]; ++i) {
fd = open(filenames[i], O_RDONLY, 0);
if (fd<0)
continue;
n = read_all(fd, buf, sizeof(buf));
close(fd);
if (n != sizeof(buf))
return -1;
arc4_addrandom(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
arc4_seeded_ok = 1;
return 0;
}
return -1;
#endif
}
static void
arc4_stir(void)
{
int i;
if (!rs_initialized) {
arc4_init();
rs_initialized = 1;
}
arc4_seed();
/*
* Discard early keystream, as per recommendations in
* "Weaknesses in the Key Scheduling Algorithm of RC4" by
* Scott Fluhrer, Itsik Mantin, and Adi Shamir.
* http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
*
* Ilya Mironov's "(Not So) Random Shuffles of RC4" suggests that
* we drop at least 2*256 bytes, with 12*256 as a conservative
* value.
*
* RFC4345 says to drop 6*256.
*
* At least some versions of this code drop 4*256, in a mistaken
* belief that "words" in the Fluhrer/Mantin/Shamir paper refers
* to processor words.
*
* We add another sect to the cargo cult, and choose 12*256.
*/
for (i = 0; i < 12*256; i++)
(void)arc4_getbyte();
arc4_count = BYTES_BEFORE_RESEED;
}
static void
arc4_stir_if_needed(void)
{
pid_t pid = getpid();
if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid)
{
arc4_stir_pid = pid;
arc4_stir();
}
}
static inline unsigned char
arc4_getbyte(void)
{
unsigned char si, sj;
rs.i = (rs.i + 1);
si = rs.s[rs.i];
rs.j = (rs.j + si);
sj = rs.s[rs.j];
rs.s[rs.i] = sj;
rs.s[rs.j] = si;
return (rs.s[(si + sj) & 0xff]);
}
static inline unsigned int
arc4_getword(void)
{
unsigned int val;
val = arc4_getbyte() << 24;
val |= arc4_getbyte() << 16;
val |= arc4_getbyte() << 8;
val |= arc4_getbyte();
return val;
}
#ifndef ARC4RANDOM_NOSTIR
ARC4RANDOM_EXPORT int
arc4random_stir(void)
{
int val;
_ARC4_LOCK();
val = arc4_stir();
_ARC4_UNLOCK();
return val;
}
#endif
#ifndef ARC4RANDOM_NOADDRANDOM
ARC4RANDOM_EXPORT void
arc4random_addrandom(const unsigned char *dat, int datlen)
{
int j;
_ARC4_LOCK();
if (!rs_initialized)
arc4_stir();
for (j = 0; j < datlen; j += 256) {
/* arc4_addrandom() ignores all but the first 256 bytes of
* its input. We want to make sure to look at ALL the
* data in 'dat', just in case the user is doing something
* crazy like passing us all the files in /var/log. */
arc4_addrandom(dat + j, datlen - j);
}
_ARC4_UNLOCK();
}
#endif
#ifndef ARC4RANDOM_NORANDOM
ARC4RANDOM_EXPORT ARC4RANDOM_UINT32
arc4random(void)
{
ARC4RANDOM_UINT32 val;
_ARC4_LOCK();
arc4_count -= 4;
arc4_stir_if_needed();
val = arc4_getword();
_ARC4_UNLOCK();
return val;
}
#endif
ARC4RANDOM_EXPORT void
arc4random_buf(void *_buf, size_t n)
{
unsigned char *buf = _buf;
_ARC4_LOCK();
arc4_stir_if_needed();
while (n--) {
if (--arc4_count <= 0)
arc4_stir();
buf[n] = arc4_getbyte();
}
_ARC4_UNLOCK();
}
#ifndef ARC4RANDOM_NOUNIFORM
/*
* Calculate a uniformly distributed random number less than upper_bound
* avoiding "modulo bias".
*
* Uniformity is achieved by generating new random numbers until the one
* returned is outside the range [0, 2**32 % upper_bound). This
* guarantees the selected random number will be inside
* [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
* after reduction modulo upper_bound.
*/
ARC4RANDOM_EXPORT unsigned int
arc4random_uniform(unsigned int upper_bound)
{
ARC4RANDOM_UINT32 r, min;
if (upper_bound < 2)
return 0;
#if (UINT_MAX > 0xffffffffUL)
min = 0x100000000UL % upper_bound;
#else
/* Calculate (2**32 % upper_bound) avoiding 64-bit math */
if (upper_bound > 0x80000000)
min = 1 + ~upper_bound; /* 2**32 - upper_bound */
else {
/* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
}
#endif
/*
* This could theoretically loop forever but each retry has
* p > 0.5 (worst case, usually far better) of selecting a
* number inside the range we need, so it should rarely need
* to re-roll.
*/
for (;;) {
r = arc4random();
if (r >= min)
break;
}
return r % upper_bound;
}
#endif

View File

@ -183,7 +183,7 @@ AC_C_INLINE
AC_HEADER_TIME AC_HEADER_TIME
dnl Checks for library functions. dnl Checks for library functions.
AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random issetugid geteuid getegid getservbyname getprotobynumber setenv unsetenv putenv) AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo getnameinfo strlcpy inet_ntop inet_pton signal sigaction strtoll inet_aton pipe eventfd sendfile mmap splice arc4random arc4random_buf issetugid geteuid getegid getservbyname getprotobynumber setenv unsetenv putenv)
# Check for gethostbyname_r in all its glorious incompatible versions. # Check for gethostbyname_r in all its glorious incompatible versions.
# (This is cut-and-pasted from Tor, which based its logic on # (This is cut-and-pasted from Tor, which based its logic on
@ -244,14 +244,6 @@ AC_CHECK_FUNC(gethostbyname_r, [
AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long)
if test "x$ac_cv_func_arc4random" = "xyes" ; then
AC_DEFINE(DNS_USE_ARC4RANDOM_FOR_ID, 1, [Define if we should use arc4random to generate dns transation IDs])
elif test "x$ac_cv_func_clock_gettime" = "xyes"; then
AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if we should use clock_gettime to generate dns transation IDs])
else
AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define if s no secure id variant is available])
fi
AC_MSG_CHECKING(for F_SETFD in fcntl.h) AC_MSG_CHECKING(for F_SETFD in fcntl.h)
AC_EGREP_CPP(yes, AC_EGREP_CPP(yes,
[ [

132
evdns.c
View File

@ -37,45 +37,6 @@
#include <sys/types.h> #include <sys/types.h>
#include "event-config.h" #include "event-config.h"
#ifdef _EVENT_DNS_USE_FTIME_FOR_ID
#include <sys/timeb.h>
#endif
#ifndef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID
#ifndef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID
#ifndef _EVENT_DNS_USE_OPENSSL_FOR_ID
#ifndef _EVENT_DNS_USE_FTIME_FOR_ID
#ifndef _EVENT_DNS_USE_ARC4RANDOM_FOR_ID
#error Must configure at least one id generation method.
#error Please see the documentation.
#endif
#endif
#endif
#endif
#endif
/* #define _POSIX_C_SOURCE 200507 */
#define _GNU_SOURCE
/* for strtok_r */
#define _REENTRANT
#ifdef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID
#ifdef _EVENT_DNS_USE_OPENSSL_FOR_ID
#error Multiple id options selected
#endif
#ifdef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID
#error Multiple id options selected
#endif
#include <time.h>
#endif
#ifdef _EVENT_DNS_USE_OPENSSL_FOR_ID
#ifdef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID
#error Multiple id options selected
#endif
#include <openssl/rand.h>
#endif
#ifndef _FORTIFY_SOURCE #ifndef _FORTIFY_SOURCE
#define _FORTIFY_SOURCE 3 #define _FORTIFY_SOURCE 3
#endif #endif
@ -1213,97 +1174,15 @@ err:
#undef GET8 #undef GET8
} }
static u16
default_transaction_id_fn(void)
{
u16 trans_id;
#ifdef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID
struct timespec ts;
static int clkid = -1;
if (clkid == -1) {
clkid = CLOCK_REALTIME;
#ifdef CLOCK_MONOTONIC
if (clock_gettime(CLOCK_MONOTONIC, &ts) != -1)
clkid = CLOCK_MONOTONIC;
#endif
}
if (clock_gettime(clkid, &ts) == -1)
event_err(1, "clock_gettime");
trans_id = ts.tv_nsec & 0xffff;
#endif
#ifdef _EVENT_DNS_USE_FTIME_FOR_ID
struct _timeb tb;
_ftime(&tb);
trans_id = tb.millitm & 0xffff;
#endif
#ifdef _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID
struct timeval tv;
evutil_gettimeofday(&tv, NULL);
trans_id = tv.tv_usec & 0xffff;
#endif
#ifdef _EVENT_DNS_USE_OPENSSL_FOR_ID
if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
/* in the case that the RAND call fails we used to back */
/* down to using gettimeofday. */
/*
struct timeval tv;
gettimeofday(&tv, NULL);
trans_id = tv.tv_usec & 0xffff;
*/
event_errx("RAND_pseudo_bytes failed!");
}
#endif
#ifdef _EVENT_DNS_USE_ARC4RANDOM_FOR_ID
trans_id = arc4random() & 0xffff;
#endif
return trans_id;
}
static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn;
static void
default_random_bytes_fn(char *buf, size_t n)
{
unsigned i;
for (i = 0; i < n; i += 2) {
u16 tid = trans_id_function();
buf[i] = (tid >> 8) & 0xff;
if (i+1<n)
buf[i+1] = tid & 0xff;
}
}
static void (*rand_bytes_function)(char *buf, size_t n) =
default_random_bytes_fn;
static u16
trans_id_from_random_bytes_fn(void)
{
u16 tid;
rand_bytes_function((char*) &tid, sizeof(tid));
return tid;
}
void void
evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)) evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void))
{ {
if (fn)
trans_id_function = fn;
else
trans_id_function = default_transaction_id_fn;
rand_bytes_function = default_random_bytes_fn;
} }
void void
evdns_set_random_bytes_fn(void (*fn)(char *, size_t)) evdns_set_random_bytes_fn(void (*fn)(char *, size_t))
{ {
rand_bytes_function = fn;
trans_id_function = trans_id_from_random_bytes_fn;
} }
/* Try to choose a strong transaction id which isn't already in flight */ /* Try to choose a strong transaction id which isn't already in flight */
@ -1311,7 +1190,8 @@ static u16
transaction_id_pick(struct evdns_base *base) { transaction_id_pick(struct evdns_base *base) {
ASSERT_LOCKED(base); ASSERT_LOCKED(base);
for (;;) { for (;;) {
u16 trans_id = trans_id_function(); u16 trans_id;
evutil_secure_rng_get_bytes(&trans_id, sizeof(trans_id));
if (trans_id == 0xffff) continue; if (trans_id == 0xffff) continue;
/* now check to see if that id is already inflight */ /* now check to see if that id is already inflight */
@ -2675,7 +2555,7 @@ request_new(struct evdns_base *base, int type, const char *name, int flags,
unsigned i; unsigned i;
char randbits[(sizeof(namebuf)+7)/8]; char randbits[(sizeof(namebuf)+7)/8];
strlcpy(namebuf, name, sizeof(namebuf)); strlcpy(namebuf, name, sizeof(namebuf));
rand_bytes_function(randbits, (name_len+7)/8); evutil_secure_rng_get_bytes(randbits, (name_len+7)/8);
for (i = 0; i < name_len; ++i) { for (i = 0; i < name_len; ++i) {
if (EVUTIL_ISALPHA(namebuf[i])) { if (EVUTIL_ISALPHA(namebuf[i])) {
if ((randbits[i >> 3] & (1<<(i & 7)))) if ((randbits[i >> 3] & (1<<(i & 7))))
@ -3720,6 +3600,12 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
{ {
struct evdns_base *base; struct evdns_base *base;
if (evutil_secure_rng_init() < 0) {
log(EVDNS_LOG_WARN, "Unable to seed random number generator; "
"DNS can't run.");
return NULL;
}
/* Give the evutil library a hook into its evdns-enabled /* Give the evutil library a hook into its evdns-enabled
* functionality. We can't just call evdns_getaddrinfo directly or * functionality. We can't just call evdns_getaddrinfo directly or
* else libevent-core will depend on libevent-extras. */ * else libevent-core will depend on libevent-extras. */

View File

@ -1859,9 +1859,9 @@ long
_evutil_weakrand(void) _evutil_weakrand(void)
{ {
#ifdef WIN32 #ifdef WIN32
return rand(); return rand();
#else #else
return random(); return random();
#endif #endif
} }

121
evutil_rand.c Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2007-2010 Niels Provos and Nick Mathewson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This file has our secure PRNG code. On platforms that have arc4random(),
* we just use that. Otherwise, we include arc4random.c as a bunch of static
* functions, and wrap it lightly. We don't expose the arc4random*() APIs
* because A) they aren't in our namespace, and B) it's not nice to name your
* APIs after their implementations. We keep them in a separate file
* so that other people can rip it out and use it for whatever.
*/
#include "event-config.h"
#include "util-internal.h"
#include "evthread-internal.h"
#ifdef _EVENT_HAVE_ARC4RANDOM
#include <stdlib.h>
#include <string.h>
int
evutil_secure_rng_init(void)
{
return 0;
}
#ifndef _EVENT_HAVE_ARC4RANDOM_BUF
static void
arc4random_buf(void *buf, size_t n)
{
unsigned char *b = buf;
/* Make sure that we start out with b at a 4-byte alignment; plenty
* of CPUs care about this for 32-bit access. */
if (n >= 4 && ((ev_uintptr_t)b) & 3) {
ev_uint32_t u = arc4random();
int n_bytes = 4 - (((ev_uintptr_t)b) & 3);
memcpy(b, &u, n_bytes);
b += n_bytes;
n -= n_bytes;
}
while (n >= 4) {
*(ev_uint32_t*)b = arc4random();
b += 4;
n -= 4;
}
if (n) {
ev_uint32_t u = arc4random();
memcpy(b, &u, n);
}
}
#endif
#else /* !_EVENT_HAVE_ARC4RANDOM { */
#ifdef _EVENT_ssize_t
#define ssize_t _EVENT_SSIZE_t
#endif
#define ARC4RANDOM_EXPORT static
#define _ARC4_LOCK() EVLOCK_LOCK(arc4rand_lock, 0)
#define _ARC4_UNLOCK() EVLOCK_UNLOCK(arc4rand_lock, 0)
static void *arc4rand_lock;
#define ARC4RANDOM_UINT32 ev_uint32_t
#define ARC4RANDOM_NOSTIR
#define ARC4RANDOM_NORANDOM
#define ARC4RANDOM_NOUNIFORM
#include "./arc4random.c"
int
evutil_secure_rng_init(void)
{
int val;
if (!arc4rand_lock) {
EVTHREAD_ALLOC_LOCK(arc4rand_lock, 0);
}
_ARC4_LOCK();
if (!arc4_seeded_ok)
arc4_stir();
val = arc4_seeded_ok ? 0 : -1;
_ARC4_UNLOCK();
return val;
}
#endif /* } !_EVENT_HAVE_ARC4RANDOM */
void
evutil_secure_rng_get_bytes(void *buf, size_t n)
{
arc4random_buf(buf, n);
}
void
evutil_secure_rng_add_bytes(const char *buf, size_t n)
{
arc4random_addrandom((unsigned char*)buf, n);
}

View File

@ -58,31 +58,6 @@
* (see http://www.imperialviolet.org/page25.html#e498). Otherwise, * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
* please continue. * please continue.
* *
* This code is based on Libevent and you must call event_init before
* any of the APIs in this file. You must also seed the OpenSSL random
* source if you are using OpenSSL for ids (see below).
*
* This library is designed to be included and shipped with your source
* code. You statically link with it. You should also test for the
* existence of strtok_r and define HAVE_STRTOK_R if you have it.
*
* The DNS protocol requires a good source of id numbers and these
* numbers should be unpredictable for spoofing reasons. There are
* three methods for generating them here and you must define exactly
* one of them. In increasing order of preference:
*
* DNS_USE_GETTIMEOFDAY_FOR_ID:
* Using the bottom 16 bits of the usec result from gettimeofday. This
* is a pretty poor solution but should work anywhere.
* DNS_USE_CPU_CLOCK_FOR_ID:
* Using the bottom 16 bits of the nsec result from the CPU's time
* counter. This is better, but may not work everywhere. Requires
* POSIX realtime support and you'll need to link against -lrt on
* glibc systems at least.
* DNS_USE_OPENSSL_FOR_ID:
* Uses the OpenSSL RAND_bytes call to generate the data. You must
* have seeded the pool before making any calls to this library.
*
* The library keeps track of the state of nameservers and will avoid * The library keeps track of the state of nameservers and will avoid
* them when they go down. Otherwise it will round robin between them. * them when they go down. Otherwise it will round robin between them.
* *
@ -513,6 +488,9 @@ void evdns_set_log_fn(evdns_debug_log_fn_type fn);
is bad for security. is bad for security.
@param fn the new callback, or NULL to use the default. @param fn the new callback, or NULL to use the default.
NOTE: This function has no effect in Libevent 2.0.4-alpha and later,
since Libevent now provides its own secure RNG.
*/ */
void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
@ -521,6 +499,9 @@ void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
the same function as passed to evdns_set_transaction_id_fn to generate the same function as passed to evdns_set_transaction_id_fn to generate
bytes two at a time. If a function is provided here, it's also used bytes two at a time. If a function is provided here, it's also used
to generate transaction IDs. to generate transaction IDs.
NOTE: This function has no effect in Libevent 2.0.4-alpha and later,
since Libevent now provides its own secure RNG.
*/ */
void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); void evdns_set_random_bytes_fn(void (*fn)(char *, size_t));

View File

@ -525,6 +525,18 @@ void evutil_freeaddrinfo(struct evutil_addrinfo *ai);
const char *evutil_gai_strerror(int err); const char *evutil_gai_strerror(int err);
/* Generate n bytes of secure pseudorandom data, and store them in buf.
*
* By default, Libevent uses an ARC4-based random number generator, seeded
* using the platform's entropy source (/dev/urandom on Unix-like systems;
* CryptGenRandom on Windows).
*/
void evutil_secure_rng_get_bytes(void *buf, size_t n);
int evutil_secure_rng_init(void);
void evutil_secure_rng_add_bytes(const char *dat, size_t datlen);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -704,6 +704,7 @@ end:
evdns_close_server_port(port2); evdns_close_server_port(port2);
} }
#if 0
static void static void
dumb_bytes_fn(char *p, size_t n) dumb_bytes_fn(char *p, size_t n)
{ {
@ -713,6 +714,7 @@ dumb_bytes_fn(char *p, size_t n)
for(i=0;i<n;++i) for(i=0;i<n;++i)
p[i] = (char)(rand() & 7); p[i] = (char)(rand() & 7);
} }
#endif
static void static void
dns_inflight_test(void *arg) dns_inflight_test(void *arg)
@ -727,9 +729,11 @@ dns_inflight_test(void *arg)
tt_assert(regress_dnsserver(base, &portnum, reissue_table)); tt_assert(regress_dnsserver(base, &portnum, reissue_table));
#if 0
/* Make sure that having another (very bad!) RNG doesn't mess us /* Make sure that having another (very bad!) RNG doesn't mess us
* up. */ * up. */
evdns_set_random_bytes_fn(dumb_bytes_fn); evdns_set_random_bytes_fn(dumb_bytes_fn);
#endif
dns = evdns_base_new(base, 0); dns = evdns_base_new(base, 0);
tt_assert(!evdns_base_nameserver_ip_add(dns, "127.0.0.1:53900")); tt_assert(!evdns_base_nameserver_ip_add(dns, "127.0.0.1:53900"));