From 87c9d4de14d6d97b08db95ff4fcd7611508d3018 Mon Sep 17 00:00:00 2001 From: Jonathan Curran Date: Fri, 4 Sep 2015 12:54:21 +0000 Subject: [PATCH] Use arc4random instead of res_randomid. arc4random implementation was copied from opensmtpd/openbsd-compat --- configure.ac | 4 + openbsd-compat/NOTES | 1 + openbsd-compat/arc4random.c | 294 ++++++++++++++++++++++++++++++++ openbsd-compat/defines.h | 9 + openbsd-compat/openbsd-compat.h | 19 ++- src/res_mkquery.c | 2 +- src/res_send_async.c | 2 +- 7 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 openbsd-compat/arc4random.c diff --git a/configure.ac b/configure.ac index 10aff04..a5e2681 100644 --- a/configure.ac +++ b/configure.ac @@ -442,6 +442,10 @@ fi #l1572 (customized) dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS([ \ + arc4random \ + arc4random_buf \ + arc4random_stir \ + arc4random_uniform \ asprintf \ bcopy \ explicit_bzero \ diff --git a/openbsd-compat/NOTES b/openbsd-compat/NOTES index baa60ec..869b045 100644 --- a/openbsd-compat/NOTES +++ b/openbsd-compat/NOTES @@ -1,5 +1,6 @@ List of files and where they come from +arc4random.c portable openssh clock_gettime.c handmade defines.h portable openssh fgetln.c part of /usr/src/usr.bin/make/util.c diff --git a/openbsd-compat/arc4random.c b/openbsd-compat/arc4random.c new file mode 100644 index 0000000..fa0d630 --- /dev/null +++ b/openbsd-compat/arc4random.c @@ -0,0 +1,294 @@ +/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */ + +/* $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres + * Copyright (c) 2008, Damien Miller + * Copyright (c) 2013, Markus Friedl + * + * 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. + */ + +/* + * ChaCha based random number generator for OpenBSD. + */ + +#include "includes.h" + +#include +#include +#include +#include + +#ifndef HAVE_ARC4RANDOM + +#include +#include + +#include "log.h" + +#define KEYSTREAM_ONLY +#include "chacha_private.h" + +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +/* OpenSSH isn't multithreaded */ +#define _ARC4_LOCK() +#define _ARC4_UNLOCK() + +#define KEYSZ 32 +#define IVSZ 8 +#define BLOCKSZ 64 +#define RSBUFSZ (16*BLOCKSZ) +static int rs_initialized; +static pid_t rs_stir_pid; +static chacha_ctx rs; /* chacha context for random keystream */ +static u_char rs_buf[RSBUFSZ]; /* keystream blocks */ +static size_t rs_have; /* valid bytes at end of rs_buf */ +static size_t rs_count; /* bytes till reseed */ + +static inline void _rs_rekey(u_char *dat, size_t datlen); + +static inline void +_rs_init(u_char *buf, size_t n) +{ + if (n < KEYSZ + IVSZ) + return; + chacha_keysetup(&rs, buf, KEYSZ * 8, 0); + chacha_ivsetup(&rs, buf + KEYSZ); +} + +static void +_rs_stir(void) +{ + u_char rnd[KEYSZ + IVSZ]; + + if (RAND_bytes(rnd, sizeof(rnd)) <= 0) + fatal("Couldn't obtain random bytes (error %ld)", + ERR_get_error()); + + if (!rs_initialized) { + rs_initialized = 1; + _rs_init(rnd, sizeof(rnd)); + } else + _rs_rekey(rnd, sizeof(rnd)); + memset(rnd, 0, sizeof(rnd)); + + /* invalidate rs_buf */ + rs_have = 0; + memset(rs_buf, 0, RSBUFSZ); + + rs_count = 1600000; +} + +static inline void +_rs_stir_if_needed(size_t len) +{ + pid_t pid = getpid(); + + if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) { + rs_stir_pid = pid; + _rs_stir(); + } else + rs_count -= len; +} + +static inline void +_rs_rekey(u_char *dat, size_t datlen) +{ +#ifndef KEYSTREAM_ONLY + memset(rs_buf, 0,RSBUFSZ); +#endif + /* fill rs_buf with the keystream */ + chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ); + /* mix in optional user provided data */ + if (dat) { + size_t i, m; + + m = MIN(datlen, KEYSZ + IVSZ); + for (i = 0; i < m; i++) + rs_buf[i] ^= dat[i]; + } + /* immediately reinit for backtracking resistance */ + _rs_init(rs_buf, KEYSZ + IVSZ); + memset(rs_buf, 0, KEYSZ + IVSZ); + rs_have = RSBUFSZ - KEYSZ - IVSZ; +} + +static inline void +_rs_random_buf(void *_buf, size_t n) +{ + u_char *buf = (u_char *)_buf; + size_t m; + + _rs_stir_if_needed(n); + while (n > 0) { + if (rs_have > 0) { + m = MIN(n, rs_have); + memcpy(buf, rs_buf + RSBUFSZ - rs_have, m); + memset(rs_buf + RSBUFSZ - rs_have, 0, m); + buf += m; + n -= m; + rs_have -= m; + } + if (rs_have == 0) + _rs_rekey(NULL, 0); + } +} + +static inline void +_rs_random_u32(u_int32_t *val) +{ + _rs_stir_if_needed(sizeof(*val)); + if (rs_have < sizeof(*val)) + _rs_rekey(NULL, 0); + memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val)); + memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val)); + rs_have -= sizeof(*val); + return; +} + +void +arc4random_stir(void) +{ + _ARC4_LOCK(); + _rs_stir(); + _ARC4_UNLOCK(); +} + +void +arc4random_addrandom(u_char *dat, int datlen) +{ + int m; + + _ARC4_LOCK(); + if (!rs_initialized) + _rs_stir(); + while (datlen > 0) { + m = MIN(datlen, KEYSZ + IVSZ); + _rs_rekey(dat, m); + dat += m; + datlen -= m; + } + _ARC4_UNLOCK(); +} + +u_int32_t +arc4random(void) +{ + u_int32_t val; + + _ARC4_LOCK(); + _rs_random_u32(&val); + _ARC4_UNLOCK(); + return val; +} + +/* + * If we are providing arc4random, then we can provide a more efficient + * arc4random_buf(). + */ +# ifndef HAVE_ARC4RANDOM_BUF +void +arc4random_buf(void *buf, size_t n) +{ + _ARC4_LOCK(); + _rs_random_buf(buf, n); + _ARC4_UNLOCK(); +} +# endif /* !HAVE_ARC4RANDOM_BUF */ +#endif /* !HAVE_ARC4RANDOM */ + +/* arc4random_buf() that uses platform arc4random() */ +#if !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) +void +arc4random_buf(void *_buf, size_t n) +{ + size_t i; + u_int32_t r = 0; + char *buf = (char *)_buf; + + for (i = 0; i < n; i++) { + if (i % 4 == 0) + r = arc4random(); + buf[i] = r & 0xff; + r >>= 8; + } + explicit_bzero(&r, sizeof(r)); +} +#endif /* !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) */ + +#ifndef HAVE_ARC4RANDOM_UNIFORM +/* + * 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. + */ +u_int32_t +arc4random_uniform(u_int32_t upper_bound) +{ + u_int32_t r, min; + + if (upper_bound < 2) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + min = -upper_bound % upper_bound; + + /* + * 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 /* !HAVE_ARC4RANDOM_UNIFORM */ + +#if 0 +/*-------- Test code for i386 --------*/ +#include +#include +int +main(int argc, char **argv) +{ + const int iter = 1000000; + int i; + pctrval v; + + v = rdtsc(); + for (i = 0; i < iter; i++) + arc4random(); + v = rdtsc() - v; + v /= iter; + + printf("%qd cycles\n", v); + exit(0); +} +#endif diff --git a/openbsd-compat/defines.h b/openbsd-compat/defines.h index da7a42c..e79ef51 100644 --- a/openbsd-compat/defines.h +++ b/openbsd-compat/defines.h @@ -745,6 +745,15 @@ struct winsize { #define INET6_ADDRSTRLEN 46 #endif +/* + * Platforms that have arc4random_uniform() and not arc4random_stir() + * shouldn't need the latter. + */ +#if defined(HAVE_ARC4RANDOM) && defined(HAVE_ARC4RANDOM_UNIFORM) && \ + !defined(HAVE_ARC4RANDOM_STIR) +# define arc4random_stir() +#endif + #ifndef HAVE_VA_COPY # ifdef HAVE___VA_COPY # define va_copy(dest, src) __va_copy(dest, src) diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index c30591c..a08502d 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h @@ -63,7 +63,24 @@ char *strsep(char **stringp, const char *delim); #ifndef HAVE_ASPRINTF int asprintf(char **, const char *, ...); -#endif +#endif + +#ifdef HAVE_ARC4RANDOM +# ifndef HAVE_ARC4RANDOM_STIR +# define arc4random_stir() +# endif +#else +unsigned int arc4random(void); +void arc4random_stir(void); +#endif /* !HAVE_ARC4RANDOM */ + +#ifndef HAVE_ARC4RANDOM_BUF +void arc4random_buf(void *, size_t); +#endif + +#ifndef HAVE_ARC4RANDOM_UNIFORM +u_int32_t arc4random_uniform(u_int32_t); +#endif /* #include XXX needed? For size_t */ diff --git a/src/res_mkquery.c b/src/res_mkquery.c index 27ed21e..cce4029 100644 --- a/src/res_mkquery.c +++ b/src/res_mkquery.c @@ -57,7 +57,7 @@ res_mkquery(int op, const char *dname, int class, int type, ac = asr_use_resolver(NULL); memset(&h, 0, sizeof h); - h.id = res_randomid(); + h.id = arc4random(); if (ac->ac_options & RES_RECURSE) h.flags |= RD_MASK; h.qdcount = 1; diff --git a/src/res_send_async.c b/src/res_send_async.c index a60aa0d..a0f4704 100644 --- a/src/res_send_async.c +++ b/src/res_send_async.c @@ -380,7 +380,7 @@ setup_query(struct asr_query *as, const char *name, const char *dom, as->as.dns.obuflen = 0; memset(&h, 0, sizeof h); - h.id = res_randomid(); + h.id = arc4random(); if (as->as_ctx->ac_options & RES_RECURSE) h.flags |= RD_MASK; h.qdcount = 1;