diff options
Diffstat (limited to 'plugins/MirOTR/Libgcrypt/random/rndlinux.c')
-rw-r--r-- | plugins/MirOTR/Libgcrypt/random/rndlinux.c | 182 |
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 */ } |