From c5351ea4c69ad43dbad6179760307d98eeddbb4f Mon Sep 17 00:00:00 2001 From: william Date: Sat, 19 Apr 2014 10:55:01 -0700 Subject: add rand.stir, which polls system entropy in the most robust way possible, because OpenSSL only knows how to use /dev/urandom --- src/openssl.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 207 insertions(+), 20 deletions(-) diff --git a/src/openssl.c b/src/openssl.c index 432c683..967665f 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -26,23 +26,32 @@ #ifndef LUAOSSL_H #define LUAOSSL_H -#include /* INT_MAX INT_MIN */ -#include /* memset(3) */ -#include /* strcasecmp(3) */ -#include /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ -#include /* struct tm time_t strptime(3) */ -#include /* tolower(3) */ +#include /* INT_MAX INT_MIN */ +#include /* memset(3) strerror_r(3) */ +#include /* strcasecmp(3) */ +#include /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ +#include /* struct tm time_t strptime(3) */ +#include /* tolower(3) */ +#include /* errno */ -#include -#include /* struct stat stat(2) */ -#include /* AF_INET AF_INET6 */ +#include /* ssize_t pid_t */ +#include /* CTL_KERN KERN_RANDOM RANDOM_UUID KERN_URND KERN_ARND sysctl(2) */ +#include /* struct timeval gettimeofday(2) */ +#include /* struct stat stat(2) */ +#include /* AF_INET AF_INET6 */ +#include /* RUSAGE_SELF struct rusage getrusage(2) */ +#include /* struct utsname uname(3) */ -#include /* struct in_addr struct in6_addr */ -#include /* inet_pton(3) */ +#include /* O_RDONLY O_CLOEXEC open(2) */ -#include /* pthread_mutex_init(3) pthread_mutex_lock(3) pthread_mutex_unlock(3) */ +#include /* close(2) getpid(2) */ -#include /* dladdr(3) dlopen(3) */ +#include /* struct in_addr struct in6_addr */ +#include /* inet_pton(3) */ + +#include /* pthread_mutex_init(3) pthread_mutex_lock(3) pthread_mutex_unlock(3) */ + +#include /* dladdr(3) dlopen(3) */ #include #include @@ -107,6 +116,56 @@ #define HAI SAY("hai") +#define xitoa_putc(c) do { if (p < lim) dst[p] = (c); p++; } while (0) + +static const char *xitoa(char *dst, size_t lim, long i) { + size_t p = 0; + unsigned long d = 1000000000UL, n = 0, r; + + if (i < 0) { + xitoa_putc('-'); + i *= -1; + } + + if ((i = MIN(2147483647L, i))) { + do { + if ((r = i / d) || n) { + i -= r * d; + n++; + xitoa_putc('0' + r); + } + } while (d /= 10); + } else { + xitoa_putc('0'); + } + + if (lim) + dst[MIN(p, lim - 1)] = '\0'; + + return dst; +} /* xitoa() */ + + +#define xstrerror(error) xstrerror_r((error), (char[256]){ 0 }, 256) + +static const char *xstrerror_r(int error, char *dst, size_t lim) { + static const char unknown[] = "Unknown error: "; + size_t n; + + if (0 == strerror_r(error, dst, lim) && *dst != '\0') + return dst; + + /* + * glibc snprintf can fail on memory pressure, so format our number + * manually. + */ + n = MIN(sizeof unknown - 1, lim); + memcpy(dst, unknown, n); + + return xitoa(&dst[n], lim - n, error); +} /* xstrerror_r() */ + + static void *prepudata(lua_State *L, size_t size, const char *tname, int (*gc)(lua_State *)) { void *p = memset(lua_newuserdata(L, size), 0, size); @@ -3017,7 +3076,7 @@ static int xs_add(lua_State *L) { int ok; if (0 != stat(path, &st)) - return luaL_error(L, "%s: %s", path, strerror(errno)); + return luaL_error(L, "%s: %s", path, xstrerror(errno)); if (S_ISDIR(st.st_mode)) ok = X509_STORE_load_locations(store, NULL, path); @@ -3919,6 +3978,137 @@ int luaopen__openssl_cipher(lua_State *L) { * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#ifndef HAVE_RANDOM_UUID +#define HAVE_RANDOM_UUID (defined __linux) /* RANDOM_UUID is an enum, not macro */ +#endif + +#ifndef HAVE_KERN_URND +#define HAVE_KERN_URND (defined KERN_URND) +#endif + +#ifndef HAVE_KERN_ARND +#define HAVE_KERN_ARND (defined KERN_ARND) +#endif + +static int stir(unsigned rqstd) { + unsigned count = 0; + int error; + unsigned char data[256]; +#if HAVE_RANDOM_UUID || HAVE_KERN_URND || HAVE_KERN_ARND +#if HAVE_RANDOM_UUID + int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID }; +#elif HAVE_KERN_URND + int mib[] = { CTL_KERN, KERN_URND }; +#else + int mib[] = { CTL_KERN, KERN_ARND }; +#endif + + while (count < rqstd) { + size_t n = MIN(rqstd - count, sizeof data); + + if (0 != sysctl(mib, countof(mib), data, &n, (void *)0, 0)) + break; + + RAND_add(data, n, n); + + count += n; + } +#endif + + if (count < rqstd) { +#if defined O_CLOEXEC + int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); +#else + int fd = open("/dev/urandom", O_RDONLY); +#endif + + if (fd == -1) + goto syserr; + + while (count < rqstd) { + ssize_t n = read(fd, data, MIN(rqstd - count, sizeof data)); + + switch (n) { + case 0: + errno = EIO; + + /* FALL THROUGH */ + case -1: + if (errno == EINTR) + continue; + + error = errno; + + close(fd); + + goto error; + default: + RAND_add(data, n, n); + + count += n; + } + } + + close(fd); + } + + return 0; +syserr: + error = errno; +error:; + struct { + struct timeval tv; + pid_t pid; + struct rusage ru; + struct utsname un; + int (*fn)(); + } junk; + + gettimeofday(&junk.tv, NULL); + junk.pid = getpid(); + getrusage(RUSAGE_SELF, &junk.ru); + uname(&junk.un); + junk.fn = &stir; + + RAND_add(&junk, sizeof junk, 0.1); + + return error; +} /* stir() */ + + +static int rand_stir(lua_State *L) { + int error = stir(luaL_optunsigned(L, 1, 16)); + + if (error) { + lua_pushboolean(L, 0); + lua_pushstring(L, xstrerror(error)); + lua_pushinteger(L, error); + + return 3; + } else { + lua_pushboolean(L, 1); + + return 1; + } +} /* rand_stir() */ + + +static int rand_add(lua_State *L) { + const void *buf; + size_t len; + lua_Number entropy; + + buf = luaL_checklstring(L, 1, &len); + entropy = luaL_optnumber(L, 2, len); + + RAND_add(buf, len, entropy); + + lua_pushboolean(L, 1); + + return 1; +} /* rand_add() */ + + static int rand_bytes(lua_State *L) { int size = luaL_checkint(L, 1); luaL_Buffer B; @@ -4060,6 +4250,8 @@ static int rand_uniform(lua_State *L) { static const luaL_Reg rand_globals[] = { + { "stir", &rand_stir }, + { "add", &rand_add }, { "bytes", &rand_bytes }, { "ready", &rand_ready }, { "uniform", &rand_uniform }, @@ -4194,12 +4386,7 @@ static void initall(lua_State *L) { if (error == -1) { luaL_error(L, "openssl.init: %s", dlerror()); } else { - char why[256]; - - if (0 != strerror_r(error, why, sizeof why) || *why == '\0') - luaL_error(L, "openssl.init: Unknown error: %d", error); - - luaL_error(L, "openssl.init: %s", why); + luaL_error(L, "openssl.init: %s", xstrerror(error)); } } -- cgit v1.2.3-59-g8ed1b