Prefer GnuTLS when acquiring random seed

This attempts to improve on the fix for Bug#22202.
* configure.ac (HAVE_DEV_URANDOM): Remove.
Check /dev/urandom existence at run time, not at build time,
since the device could exist in the former but not the latter.
* src/sysdep.c [HAVE_GNUTLS]: Include gnutls/gnutls.h.
(gnutls_rnd) [GNUTLS_VERSION_NUMBER < 0x020c00]: New fallback macro.
(random_seed): New typedef.
(set_random_seed): New static function.
(seed_random): Use them.
(init_random): Use random_seed instead of uintmax_t, so as to
not consume more entropy than needed.  Prefer gnutls_rnd if it
works; this avoids a redundant open of /dev/urandom on
GNU/Linux with modern GnuTLS.
This commit is contained in:
Paul Eggert 2016-01-17 12:12:08 -08:00
parent a0d5b7ae3b
commit 05e8148a24
2 changed files with 43 additions and 58 deletions

View file

@ -4153,22 +4153,6 @@ fi
AC_TYPE_MBSTATE_T
AC_MSG_CHECKING([whether "/dev/urandom" is available])
dev_urandom=no
dnl MSYS, being a Cygwin fork, thinks "/dev/urandom" does exist, so
dnl don't check this for the MinGW builds.
if test "${opsys}" != "mingw32"; then
if test -r "/dev/urandom"; then
AC_DEFINE(HAVE_DEV_URANDOM, 1, [Define if the system supports the "/dev/urandom" device.])
dev_urandom=yes
fi
fi
if test $dev_urandom = yes; then
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
dnl Fixme: AC_SYS_POSIX_TERMIOS should probably be used, but it's not clear
dnl how the tty code is related to POSIX and/or other versions of termios.
dnl The following looks like a useful start.

View file

@ -99,6 +99,15 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "process.h"
#include "cm.h"
#ifdef HAVE_GNUTLS
# include <gnutls/gnutls.h>
#endif
#if 0x020c00 <= GNUTLS_VERSION_NUMBER
# include <gnutls/crypto.h>
#else
# define gnutls_rnd(level, data, len) (-1)
#endif
#ifdef WINDOWSNT
#include <direct.h>
/* In process.h which conflicts with the local copy. */
@ -2068,63 +2077,55 @@ init_signals (bool dumping)
# endif /* !HAVE_RANDOM */
#endif /* !RAND_BITS */
#ifdef HAVE_RANDOM
typedef unsigned int random_seed;
static void set_random_seed (random_seed arg) { srandom (arg); }
#elif defined HAVE_LRAND48
/* Although srand48 uses a long seed, this is unsigned long to avoid
undefined behavior on signed integer overflow in init_random. */
typedef unsigned long int random_seed;
static void set_random_seed (random_seed arg) { srand48 (arg); }
#else
typedef unsigned int random_seed;
static void set_random_seed (random_seed arg) { srand (arg); }
#endif
void
seed_random (void *seed, ptrdiff_t seed_size)
{
#if defined HAVE_RANDOM || ! defined HAVE_LRAND48
unsigned int arg = 0;
#else
long int arg = 0;
#endif
random_seed arg = 0;
unsigned char *argp = (unsigned char *) &arg;
unsigned char *seedp = seed;
ptrdiff_t i;
for (i = 0; i < seed_size; i++)
for (ptrdiff_t i = 0; i < seed_size; i++)
argp[i % sizeof arg] ^= seedp[i];
#ifdef HAVE_RANDOM
srandom (arg);
#else
# ifdef HAVE_LRAND48
srand48 (arg);
# else
srand (arg);
# endif
#endif
set_random_seed (arg);
}
void
init_random (void)
{
uintmax_t v;
struct timespec t;
bool success = false;
#if HAVE_DEV_URANDOM
FILE *fp = fopen ("/dev/urandom", "rb");
if (fp)
random_seed v;
if (gnutls_rnd (GNUTLS_RND_NONCE, &v, sizeof v) != 0)
{
int i;
for (i = 0, v = 0; i < sizeof (uintmax_t); i++)
bool success = false;
#ifndef WINDOWSNT
int fd = emacs_open ("/dev/urandom", O_RDONLY | O_BINARY, 0);
if (0 <= fd)
{
v <<= 8;
v |= fgetc (fp);
success = emacs_read (fd, &v, sizeof v) == sizeof v;
emacs_close (fd);
}
#else
success = w32_init_random (&v, sizeof v) == 0;
#endif
if (! success)
{
/* Fall back to current time value + PID. */
struct timespec t = current_timespec ();
v = getpid () ^ t.tv_sec ^ t.tv_nsec;
}
fclose (fp);
success = true;
}
#elif defined WINDOWSNT
if (w32_init_random (&v, sizeof v) == 0)
success = true;
#endif /* HAVE_DEV_URANDOM || WINDOWSNT */
if (!success)
{
/* Fall back to current time value + PID. */
t = current_timespec ();
v = getpid () ^ t.tv_sec ^ t.tv_nsec;
}
seed_random (&v, sizeof v);
set_random_seed (v);
}
/*