summaryrefslogtreecommitdiff
path: root/plugins/MirOTR/Libgcrypt/random/rndlinux.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/MirOTR/Libgcrypt/random/rndlinux.c')
-rw-r--r--plugins/MirOTR/Libgcrypt/random/rndlinux.c182
1 files changed, 133 insertions, 49 deletions
diff --git a/plugins/MirOTR/Libgcrypt/random/rndlinux.c b/plugins/MirOTR/Libgcrypt/random/rndlinux.c
index 574ef6dcc0..9eeec57465 100644
--- a/plugins/MirOTR/Libgcrypt/random/rndlinux.c
+++ b/plugins/MirOTR/Libgcrypt/random/rndlinux.c
@@ -1,5 +1,6 @@
/* rndlinux.c - raw random number for OSes with /dev/random
- * Copyright (C) 1998, 2001, 2002, 2003, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 2001, 2002, 2003, 2007,
+ * 2009 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
@@ -35,7 +36,7 @@
#include "g10lib.h"
#include "rand-internal.h"
-static int open_device ( const char *name );
+static int open_device (const char *name, int retry);
static int
@@ -53,15 +54,30 @@ set_cloexec_flag (int fd)
/*
- * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it exists)).
+ * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it
+ * exists)). If RETRY is true, the function does not terminate with
+ * a fatal error but retries until it is able to reopen the device.
*/
static int
-open_device ( const char *name )
+open_device (const char *name, int retry)
{
int fd;
- fd = open ( name, O_RDONLY );
- if ( fd == -1 )
+ if (retry)
+ _gcry_random_progress ("open_dev_random", 'X', 1, 0);
+ again:
+ fd = open (name, O_RDONLY);
+ if (fd == -1 && retry)
+ {
+ struct timeval tv;
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ _gcry_random_progress ("wait_dev_random", 'X', 0, (int)tv.tv_sec);
+ select (0, NULL, NULL, NULL, &tv);
+ goto again;
+ }
+ if (fd == -1)
log_fatal ("can't open %s: %s\n", name, strerror(errno) );
if (set_cloexec_flag (fd))
@@ -70,8 +86,8 @@ open_device ( const char *name )
/* We used to do the following check, however it turned out that this
is not portable since more OSes provide a random device which is
- sometimes implemented as another device type.
-
+ sometimes implemented as another device type.
+
struct stat sb;
if( fstat( fd, &sb ) )
@@ -83,6 +99,10 @@ open_device ( const char *name )
}
+/* Note that the caller needs to make sure that this function is only
+ called by one thread at a time. The function returns 0 on success
+ or true on failure (in which case the caller will signal a fatal
+ error). */
int
_gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
enum random_origins),
@@ -91,11 +111,32 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
{
static int fd_urandom = -1;
static int fd_random = -1;
+ static unsigned char ever_opened;
int fd;
int n;
- int warn=0;
byte buffer[768];
size_t n_hw;
+ size_t want = length;
+ size_t last_so_far = 0;
+ int any_need_entropy = 0;
+ int delay;
+
+ if (!add)
+ {
+ /* Special mode to close the descriptors. */
+ if (fd_random != -1)
+ {
+ close (fd_random);
+ fd_random = -1;
+ }
+ if (fd_urandom != -1)
+ {
+ close (fd_urandom);
+ fd_urandom = -1;
+ }
+ return 0;
+ }
+
/* First read from a hardware source. However let it account only
for up to 50% of the requested bytes. */
@@ -105,63 +146,106 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
if (length > 1)
length -= n_hw;
- /* Open the requested device. */
+ /* Open the requested device. The first time a device is to be
+ opened we fail with a fatal error if the device does not exists.
+ In case the device has ever been closed, further open requests
+ will however retry indefinitely. The rationale for this behaviour is
+ that we always require the device to be existent but want a more
+ graceful behaviour if the rarely needed close operation has been
+ used and the device needs to be re-opened later. */
if (level >= 2)
{
- if( fd_random == -1 )
- fd_random = open_device ( NAME_OF_DEV_RANDOM );
+ if (fd_random == -1)
+ {
+ fd_random = open_device (NAME_OF_DEV_RANDOM, (ever_opened & 1));
+ ever_opened |= 1;
+ }
fd = fd_random;
}
else
{
- if( fd_urandom == -1 )
- fd_urandom = open_device ( NAME_OF_DEV_URANDOM );
+ if (fd_urandom == -1)
+ {
+ fd_urandom = open_device (NAME_OF_DEV_URANDOM, (ever_opened & 2));
+ ever_opened |= 2;
+ }
fd = fd_urandom;
}
- /* And enter the read loop. */
+ /* Enter the read loop. */
+ delay = 0; /* Start with 0 seconds so that we do no block on the
+ first iteration and in turn call the progress function
+ before blocking. To give the OS a better chance to
+ return with something we will actually use 100ms. */
while (length)
{
fd_set rfds;
struct timeval tv;
int rc;
-
- FD_ZERO(&rfds);
- FD_SET(fd, &rfds);
- tv.tv_sec = 3;
- tv.tv_usec = 0;
- if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) )
+
+ /* If we collected some bytes update the progress indicator. We
+ do this always and not just if the select timed out because
+ often just a few bytes are gathered within the timeout
+ period. */
+ if (any_need_entropy || last_so_far != (want - length) )
+ {
+ last_so_far = want - length;
+ _gcry_random_progress ("need_entropy", 'X',
+ (int)last_so_far, (int)want);
+ any_need_entropy = 1;
+ }
+
+ /* If the system has no limit on the number of file descriptors
+ and we encounter an fd which is larger than the fd_set size,
+ we don't use the select at all. The select code is only used
+ to emit progress messages. A better solution would be to
+ fall back to poll() if available. */
+#ifdef FD_SETSIZE
+ if (fd < FD_SETSIZE)
+#endif
{
- if( !warn )
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = delay;
+ tv.tv_usec = delay? 0 : 100000;
+ if ( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) )
{
- _gcry_random_progress ("need_entropy", 'X', 0, (int)length);
- warn = 1;
- }
- continue;
- }
- else if( rc == -1 )
- {
- log_error ("select() error: %s\n", strerror(errno));
- continue;
- }
-
- do
- {
- int nbytes = length < sizeof(buffer)? length : sizeof(buffer);
- n = read(fd, buffer, nbytes );
- if( n >= 0 && n > nbytes )
- {
- log_error("bogus read from random device (n=%d)\n", n );
- n = nbytes;
- }
- }
- while( n == -1 && errno == EINTR );
- if( n == -1 )
- log_fatal("read error on random device: %s\n", strerror(errno));
- (*add)( buffer, n, origin );
- length -= n;
+ any_need_entropy = 1;
+ delay = 3; /* Use 3 seconds henceforth. */
+ continue;
+ }
+ else if( rc == -1 )
+ {
+ log_error ("select() error: %s\n", strerror(errno));
+ if (!delay)
+ delay = 1; /* Use 1 second if we encounter an error before
+ we have ever blocked. */
+ continue;
+ }
+ }
+
+ do
+ {
+ size_t nbytes;
+
+ nbytes = length < sizeof(buffer)? length : sizeof(buffer);
+ n = read (fd, buffer, nbytes);
+ if (n >= 0 && n > nbytes)
+ {
+ log_error("bogus read from random device (n=%d)\n", n );
+ n = nbytes;
+ }
+ }
+ while (n == -1 && errno == EINTR);
+ if (n == -1)
+ log_fatal("read error on random device: %s\n", strerror(errno));
+ (*add)(buffer, n, origin);
+ length -= n;
}
- memset(buffer, 0, sizeof(buffer) );
+ wipememory (buffer, sizeof buffer);
+
+ if (any_need_entropy)
+ _gcry_random_progress ("need_entropy", 'X', (int)want, (int)want);
return 0; /* success */
}