From 8f75d0779740d7221445ebd2da66ececb49cc05b Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 29 Dec 2018 08:04:32 -0600 Subject: Fixes #4645 -- poll() on /dev/random before reading from /dev/urandom on Linux (#4656) * Fixes #4645 -- select() on /dev/random before reading from /dev/urandom on linux * whoops * Missing header * whoops * Review notes * Potential uninitialized fix * Signals are literally impossible --- src/_cffi_src/openssl/src/osrandom_engine.c | 72 ++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 17 deletions(-) (limited to 'src/_cffi_src') diff --git a/src/_cffi_src/openssl/src/osrandom_engine.c b/src/_cffi_src/openssl/src/osrandom_engine.c index e6a76a34..315d5f1f 100644 --- a/src/_cffi_src/openssl/src/osrandom_engine.c +++ b/src/_cffi_src/openssl/src/osrandom_engine.c @@ -13,6 +13,10 @@ * Copyright 2001-2016 Python Software Foundation; All Rights Reserved. */ +#ifdef __linux__ +#include +#endif + static const char *Cryptography_osrandom_engine_id = "osrandom"; /**************************************************************************** @@ -90,9 +94,47 @@ static struct { ino_t st_ino; } urandom_cache = { -1 }; +static int set_cloexec(int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return -1; + } + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + return -1; + } + return 0; +} + +#ifdef __linux__ +/* On Linux, we open("/dev/random") and use poll() to wait until it's readable + * before we read from /dev/urandom, this ensures that we don't read from + * /dev/urandom before the kernel CSPRNG is initialized. This isn't necessary on + * other platforms because they don't have the same _bug_ as Linux does with + * /dev/urandom and early boot. */ +static int wait_on_devrandom(void) { + struct pollfd pfd = {}; + int ret = 0; + int random_fd = open("/dev/random", O_RDONLY); + if (random_fd < 0) { + return -1; + } + if (set_cloexec(random_fd) < 0) { + return -1; + } + pfd.fd = random_fd; + pfd.events = POLLIN; + pfd.revents = 0; + do { + ret = poll(&pfd, 1, -1); + } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); + close(random_fd); + return ret; +} +#endif + /* return -1 on error */ static int dev_urandom_fd(void) { - int fd, n, flags; + int fd = -1; struct stat st; /* Check that fd still points to the correct device */ @@ -106,25 +148,25 @@ static int dev_urandom_fd(void) { } } if (urandom_cache.fd < 0) { +#ifdef __linux__ + if (wait_on_devrandom() < 0) { + goto error; + } +#endif + fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { goto error; } - if (fstat(fd, &st)) { + if (set_cloexec(fd) < 0) { goto error; } - /* set CLOEXEC flag */ - flags = fcntl(fd, F_GETFD); - if (flags == -1) { - goto error; - } else if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + if (fstat(fd, &st)) { goto error; } /* Another thread initialized the fd */ if (urandom_cache.fd >= 0) { - do { - n = close(fd); - } while (n < 0 && errno == EINTR); + close(fd); return urandom_cache.fd; } urandom_cache.st_dev = st.st_dev; @@ -135,9 +177,7 @@ static int dev_urandom_fd(void) { error: if (fd != -1) { - do { - n = close(fd); - } while (n < 0 && errno == EINTR); + close(fd); } ERR_Cryptography_OSRandom_error( CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_FD, @@ -177,7 +217,7 @@ static int dev_urandom_read(unsigned char *buffer, int size) { static void dev_urandom_close(void) { if (urandom_cache.fd >= 0) { - int fd, n; + int fd; struct stat st; if (fstat(urandom_cache.fd, &st) @@ -185,9 +225,7 @@ static void dev_urandom_close(void) { && st.st_ino == urandom_cache.st_ino) { fd = urandom_cache.fd; urandom_cache.fd = -1; - do { - n = close(fd); - } while (n < 0 && errno == EINTR); + close(fd); } } } -- cgit v1.2.3