1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

Restructured the RNG source and add support for loadable

random modules.
This commit is contained in:
Werner Koch 1998-11-25 11:52:41 +00:00
parent 710d2e351b
commit 2e494682b6
11 changed files with 1101 additions and 281 deletions

View File

@ -1,6 +1,19 @@
Wed Nov 25 12:33:41 1998 Werner Koch (wk@isil.d.shuttle.de)
* rand-*.c: Removed.
* rndlinux.c : New.
* rndunix.c : New.
* random.c : Restructured the interface to the gather modules.
(intialize): Call constructor functions
(read_radnom_source): Moved to here.
* dynload.c (dynload_getfnc_gather_random): New.
(dynload_getfnc_fast_random_poll): New.
(register_internal_cipher_extension): New.
(register_cipher_extension): Support of internal modules.
Sun Nov 8 17:44:36 1998 Werner Koch (wk@isil.d.shuttle.de)
* radn-unix.c (read_random_source): Removed the assert.
* rand-unix.c (read_random_source): Removed the assert.
Mon Oct 19 18:34:30 1998 me,,, (wk@tobold)

View File

@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
gnupg_extensions = tiger twofish
gnupg_extensions = tiger twofish rndunix
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl
@ -11,7 +11,7 @@ else
pkglib_PROGRAMS =
endif
DYNLINK_MOD_CFLAGS = @DYNLINK_MOD_CFLAGS@
DYNLINK_MOD_CFLAGS = -DIS_MODULE @DYNLINK_MOD_CFLAGS@
libcipher_a_SOURCES = cipher.c \
@ -33,9 +33,7 @@ libcipher_a_SOURCES = cipher.c \
random.h \
random.c \
rand-internal.h \
rand-unix.c \
rand-w32.c \
rand-dummy.c \
rndlinux.c \
rmd.h \
rmd160.c \
sha1.h \
@ -58,6 +56,11 @@ twofish: $(srcdir)/twofish.c
sed -e 's/-O[0-9]*/ /' `
rndunix: $(srcdir)/rndunix.c
$(COMPILE) $(DYNLINK_MOD_CFLAGS) -o rndunix $(srcdir)/rndunix.c
install-exec-hook:
@list='$(pkglib_PROGRAMS)'; for p in $$list; do \
if test -f $(pkglibdir)/$$p; then \

View File

@ -39,6 +39,7 @@
typedef struct ext_list {
struct ext_list *next;
int internal;
#ifdef HAVE_DL_DLOPEN
void *handle; /* handle from dlopen() */
#else
@ -83,7 +84,7 @@ static int dld_available;
void
register_cipher_extension( const char *mainpgm, const char *fname )
{
EXTLIST r, el;
EXTLIST r, el, intex;
char *p, *pe;
#ifdef HAVE_DLD_DLD_LINK
@ -114,13 +115,53 @@ register_cipher_extension( const char *mainpgm, const char *fname )
el->hintstr = NULL;
/* check that it is not already registered */
for(r = extensions; r; r = r->next )
intex = NULL;
for(r = extensions; r; r = r->next ) {
if( !compare_filenames(r->name, el->name) ) {
log_info("extension '%s' already registered\n", el->name );
m_free(el);
return;
}
else if( r->internal )
intex = r;
}
/* and register */
/* we put them after the internal extension modules */
/* this is so that the external modules do not get loaded */
/* as soon as the internal modules are requested */
if( intex ) {
el->next = intex->next;
intex->next = el;
}
else {
el->next = extensions;
extensions = el;
}
}
void
register_internal_cipher_extension(
const char *module_id,
void * (*enumfunc)(int, int*, int*, int*)
)
{
EXTLIST r, el;
el = m_alloc_clear( sizeof *el + strlen(module_id) );
strcpy(el->name, module_id );
el->internal = 1;
/* check that it is not already registered */
for(r = extensions; r; r = r->next ) {
if( !compare_filenames(r->name, el->name) ) {
log_info("extension '%s' already registered\n", el->name );
m_free(el);
return;
}
}
/* and register */
el->enumfunc = enumfunc;
el->handle = (void*)1;
el->next = extensions;
extensions = el;
}
@ -455,3 +496,51 @@ enum_gnupgext_pubkeys( void **enum_context, int *algo,
return NULL;
}
int (*
dynload_getfnc_gather_random())(byte*, size_t*, int)
{
EXTLIST r;
void *sym;
for( r = extensions; r; r = r->next ) {
int seq, class, vers;
if( r->failed )
continue;
if( !r->handle && load_extension(r) )
continue;
seq = 0;
while( (sym = (*r->enumfunc)(40, &seq, &class, &vers)) ) {
if( vers != 1 || class != 40 )
continue;
return (int (*)(byte*, size_t*, int))sym;
}
}
return NULL;
}
void (*
dynload_getfnc_fast_random_poll())( void (*)(const void*, size_t, int))
{
EXTLIST r;
void *sym;
for( r = extensions; r; r = r->next ) {
int seq, class, vers;
if( r->failed )
continue;
if( !r->handle && load_extension(r) )
continue;
seq = 0;
while( (sym = (*r->enumfunc)(41, &seq, &class, &vers)) ) {
if( vers != 1 || class != 41 )
continue;
return (void (*)( void (*)(const void*, size_t, int)))sym;
}
}
return NULL;
}

View File

@ -20,6 +20,10 @@
#ifndef G10_CIPHER_DYNLOAD_H
#define G10_CIPHER_DYNLOAD_H
void register_internal_cipher_extension( const char *module_id,
void * (*enumfunc)(int, int*, int*, int*) );
int
enum_gnupgext_digests( void **enum_context,
int *algo,
@ -49,4 +53,10 @@ enum_gnupgext_pubkeys( void **enum_context, int *algo,
int (*cmp)(void *, MPI), void *opaquev ),
unsigned (**get_nbits)( int algo, MPI *pkey ) );
int (*dynload_getfnc_gather_random(void))(byte*, size_t*, int);
void (*dynload_getfnc_fast_random_poll(void)
)( void (*)(const void*, size_t, int));
#endif /*G10_CIPHER_DYNLOAD_H*/

View File

@ -1,134 +0,0 @@
/* rand-dummy.c - INSECURE dummy random device
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* This file is part of GNUPG.
*
* GNUPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GNUPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_GETHRTIME
#include <sys/times.h>
#endif
#ifdef HAVE_GETTIMEOFDAY
#include <sys/times.h>
#endif
#ifdef HAVE_GETRUSAGE
#include <sys/resource.h>
#endif
#include <string.h>
#include <unistd.h>
#ifdef __MINGW32__
#include <process.h>
#endif
#include <fcntl.h>
#include "util.h"
#include "ttyio.h"
#include "i18n.h"
#include "rand-internal.h"
#ifdef USE_RAND_DUMMY /* a dummy random file so we can do some tests */
#ifndef RAND_MAX /* for SunOS */
#define RAND_MAX 32767
#endif
#if __GNUC__
#warning Using the insecure dummy random device
#endif
void
random_poll()
{
char buf[POOLSIZE/5];
read_random_source( buf, POOLSIZE/5, 1 ); /* read dummy data */
add_randomness( buf, POOLSIZE/5, 2);
memset( buf, 0, POOLSIZE/5);
}
void
fast_random_poll()
{
#if HAVE_GETHRTIME
{ hrtime_t tv;
tv = gethrtime();
add_randomness( &tv, sizeof(tv), 1 );
}
#elif HAVE_GETTIMEOFDAY
{ struct timeval tv;
if( gettimeofday( &tv, NULL ) )
BUG();
add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 );
}
#else /* use times */
{
#ifndef __MINGW32__
struct tms buf;
times( &buf );
add_randomness( &buf, sizeof buf, 1 );
#endif
}
#endif
#ifdef HAVE_GETRUSAGE
{ struct rusage buf;
if( getrusage( RUSAGE_SELF, &buf ) )
BUG();
add_randomness( &buf, sizeof buf, 1 );
memset( &buf, 0, sizeof buf );
}
#endif
}
void
read_random_source( byte *buffer, size_t length, int level )
{
static int initialized=0;
if( !initialized ) {
log_info(_("WARNING: using insecure random number generator!!\n"));
tty_printf(_("The random number generator is only a kludge to let\n"
"it compile - it is in no way a strong RNG!\n\n"
"DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n\n"));
initialized=1;
#ifdef HAVE_RAND
srand(make_timestamp()*getpid());
#else
srandom(make_timestamp()*getpid());
#endif
}
#ifdef HAVE_RAND
while( length-- )
*buffer++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1);
#else
while( length-- )
*buffer++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1);
#endif
}
#endif /* USE_RAND_DUMMY */

View File

@ -20,34 +20,11 @@
#ifndef G10_RAND_INTERNAL_H
#define G10_RAND_INTERNAL_H
/* For now we use the DUMMY random generator if we do not have
* the real random device */
#ifndef HAVE_DEV_RANDOM
#define USE_RAND_DUMMY 1
#undef USE_RAND_UNIX
#undef USE_RAND_W32
#endif
#include "random.h"
#define BLOCKLEN 64 /* hash this amount of bytes */
#define DIGESTLEN 20 /* into a digest of this length (rmd160) */
/* poolblocks is the number of digests which make up the pool
* and poolsize must be a multiple of the digest length
* to make the AND operations faster, the size should also be
* a multiple of ulong
*/
#define POOLBLOCKS 30
#define POOLSIZE (POOLBLOCKS*DIGESTLEN)
#if (POOLSIZE % SIZEOF_UNSIGNED_LONG)
#error Please make sure that poolsize is a multiple of ulong
#endif
#define POOLWORDS (POOLSIZE / SIZEOF_UNSIGNED_LONG)
void read_random_source( byte *buffer, size_t length, int level );
void rndlinux_constructor(void);
void rndunix_constructor(void);
void rndw32_constructor(void);
void rndos2_constructor(void);
void rndatari_constructor(void);
void rndmvs_constructor(void);
#endif /*G10_RAND_INTERNAL_H*/

View File

@ -1,54 +0,0 @@
/* rand-w32.c - Windoze32 and NT random device
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* This file is part of GNUPG.
*
* GNUPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GNUPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "util.h"
#include "rmd.h"
#include "ttyio.h"
#include "i18n.h"
#include "rand-internal.h"
#ifdef USE_RAND_W32 /* this file is only for Mingw32 */
#error To be written
void
random_poll()
{
}
void
fast_random_poll()
{
}
#endif /* USE_RAND_W32 */

View File

@ -36,7 +36,9 @@
#include "rmd.h"
#include "ttyio.h"
#include "i18n.h"
#include "random.h"
#include "rand-internal.h"
#include "dynload.h"
#if SIZEOF_UNSIGNED_LONG == 8
@ -47,6 +49,20 @@
#error weird size for an unsigned long
#endif
#define BLOCKLEN 64 /* hash this amount of bytes */
#define DIGESTLEN 20 /* into a digest of this length (rmd160) */
/* poolblocks is the number of digests which make up the pool
* and poolsize must be a multiple of the digest length
* to make the AND operations faster, the size should also be
* a multiple of ulong
*/
#define POOLBLOCKS 30
#define POOLSIZE (POOLBLOCKS*DIGESTLEN)
#if (POOLSIZE % SIZEOF_UNSIGNED_LONG)
#error Please make sure that poolsize is a multiple of ulong
#endif
#define POOLWORDS (POOLSIZE / SIZEOF_UNSIGNED_LONG)
static int is_initialized;
#define MASK_LEVEL(a) do {if( a > 2 ) a = 2; else if( a < 0 ) a = 0; } while(0)
@ -60,9 +76,16 @@ static int just_mixed;
static int secure_alloc;
static int quick_test;
static int faked_rng;
static void read_pool( byte *buffer, size_t length, int level );
static void add_randomness( const void *buffer, size_t length, int source );
static void random_poll(void);
static void read_random_source( byte *buffer, size_t length, int level );
#ifndef HAVE_DEV_RANDOM
static int gather_faked( byte *buffer, size_t *r_length, int level );
#endif
static void
@ -76,6 +99,20 @@ initialize()
keypool = secure_alloc ? m_alloc_secure_clear(POOLSIZE+BLOCKLEN)
: m_alloc_clear(POOLSIZE+BLOCKLEN);
is_initialized = 1;
#if USE_RNDLINUX
rndlinux_constructor();
#elif USE_RNDUNIX
rndunix_constructor();
#elif USE_RNDW32
rndw32_constructor();
#elif USE_RNDOS2
rndos2_constructor();
#elif USE_RNDATARI
rndatari_constructor();
#elif USE_RNDMVS
rndmvs_constructor();
#endif
}
void
@ -88,13 +125,13 @@ secure_random_alloc()
int
quick_random_gen( int onoff )
{
int last = quick_test;
int last;
read_random_source( NULL, 0, 0 ); /* load module */
last = quick_test;
if( onoff != -1 )
quick_test = onoff;
#ifdef USE_RAND_DUMMY
last = 1; /* insecure RNG */
#endif
return last;
return faked_rng? 1 : last;
}
@ -250,9 +287,9 @@ read_pool( byte *buffer, size_t length, int level )
/****************
* Add LENGTH bytes of randomness from buffer to the pool.
* source may be used to specify the randomeness source.
* source may be used to specify the randomness source.
*/
void
static void
add_randomness( const void *buffer, size_t length, int source )
{
if( !is_initialized )
@ -271,3 +308,95 @@ add_randomness( const void *buffer, size_t length, int source )
static void
random_poll()
{
char buf[POOLSIZE/5];
read_random_source( buf, POOLSIZE/5, 1 );
add_randomness( buf, POOLSIZE/5, 2);
memset( buf, 0, POOLSIZE/5);
}
void
fast_random_poll()
{
static void (*fnc)( void (*)(const void*, size_t, int)) = NULL;
static int initialized = 0;
if( !initialized ) {
if( !is_initialized )
initialize();
initialized = 1;
fnc = dynload_getfnc_fast_random_poll();
if( !fnc )
log_info("Ooops: No fast random poll function\n");
}
if( fnc )
(*fnc)( add_randomness );
}
static void
read_random_source( byte *buffer, size_t length, int level )
{
static int (*fnc)(byte*, size_t*, int) = NULL;
int nbytes;
int goodness;
if( !fnc ) {
if( !is_initialized )
initialize();
fnc = dynload_getfnc_gather_random();
if( !fnc ) {
faked_rng = 1;
#ifndef HAVE_DEV_RANDOM
fnc = gather_faked;
#else
BUG();
#endif
}
}
while( length ) {
nbytes = length;
goodness = (*fnc)( buffer, &nbytes, level );
buffer +=nbytes;
length -= nbytes;
/* FIXME: how can we handle the goodness */
}
}
#ifndef HAVE_DEV_RANDOM
static int
gather_faked( byte *buffer, size_t *r_length, int level )
{
static int initialized=0;
size_t length = *r_length;
if( !initialized ) {
log_info(_("WARNING: using insecure random number generator!!\n"));
tty_printf(_("The random number generator is only a kludge to let\n"
"it compile - it is in no way a strong RNG!\n\n"
"DON'T USE ANY DATA GENERATED BY THIS PROGRAM!!\n\n"));
initialized=1;
#ifdef HAVE_RAND
srand(make_timestamp()*getpid());
#else
srandom(make_timestamp()*getpid());
#endif
}
#ifdef HAVE_RAND
while( length-- )
*buffer++ = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1);
#else
while( length-- )
*buffer++ = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1);
#endif
return 100; /* We really fake it ;-) */
}
#endif /* ! HAVE_DEV_RANDOM */

View File

@ -27,13 +27,6 @@ void secure_random_alloc(void);
int quick_random_gen( int onoff );
void randomize_buffer( byte *buffer, size_t length, int level );
byte *get_random_bits( size_t nbits, int level, int secure );
void add_randomness( const void *buffer, size_t length, int source );
/*-- the next two functions are implemented by all the system
specific source files rand-xxxx.c --*/
void random_poll(void);
void fast_random_poll(void);
void fast_random_poll( void );
#endif /*G10_RANDOM_H*/

View File

@ -1,14 +1,14 @@
/* rand-unix.c - raw random number generator for unix like OSes
/* rndlinux.c - raw random number for OSes with /dev/random
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* This file is part of GNUPG.
* This file is part of GnuPG.
*
* GNUPG is free software; you can redistribute it and/or modify
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GNUPG is distributed in the hope that it will be useful,
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
@ -40,56 +40,47 @@
#include <unistd.h>
#include <fcntl.h>
#include "util.h"
#include "rmd.h"
#include "ttyio.h"
#include "i18n.h"
#include "rand-internal.h"
#ifdef USE_RAND_UNIX /* This file is only for real systems */
/* #define IS_MODULE 1 */
static int open_device( const char *name, int minor );
static int gather_random( byte *buffer, size_t *r_length, int level );
void
random_poll()
{
char buf[POOLSIZE/5];
read_random_source( buf, POOLSIZE/5, 1 ); /* read /dev/urandom */
add_randomness( buf, POOLSIZE/5, 2);
memset( buf, 0, POOLSIZE/5);
}
void
fast_random_poll()
static void
fast_poll( void (*add)(const void*, size_t, int) )
{
#if HAVE_GETHRTIME
{ hrtime_t tv;
tv = gethrtime();
add_randomness( &tv, sizeof(tv), 1 );
(*add)( &tv, sizeof(tv), 1 );
}
#elif HAVE_GETTIMEOFDAY
{ struct timeval tv;
if( gettimeofday( &tv, NULL ) )
BUG();
add_randomness( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
add_randomness( &tv.tv_usec, sizeof(tv.tv_usec), 1 );
(*add)( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
(*add)( &tv.tv_usec, sizeof(tv.tv_usec), 1 );
}
#else /* use times */
{ struct tms buf;
times( &buf );
add_randomness( &buf, sizeof buf, 1 );
(*add)( &buf, sizeof buf, 1 );
}
#endif
#ifdef HAVE_GETRUSAGE
{ struct rusage buf;
if( getrusage( RUSAGE_SELF, &buf ) )
BUG();
add_randomness( &buf, sizeof buf, 1 );
(*add)( &buf, sizeof buf, 1 );
memset( &buf, 0, sizeof buf );
}
#endif
}
#ifdef HAVE_DEV_RANDOM /* we have the /dev/random devices */
/****************
* Used to open the Linux and xBSD /dev/random devices
@ -115,14 +106,16 @@ open_device( const char *name, int minor )
}
void
read_random_source( byte *buffer, size_t length, int level )
static int
gather_random( byte *buffer, size_t *r_length, int level )
{
static int fd_urandom = -1;
static int fd_random = -1;
int fd;
int n;
int warn=0;
size_t length = *r_length;
/* note: we will always return the requested length */
if( level >= 2 ) {
if( fd_random == -1 )
@ -170,28 +163,75 @@ read_random_source( byte *buffer, size_t length, int level )
buffer += n;
length -= n;
} while( length );
return 100; /* 100% useful at the requested level */
}
#else /* not HAVE_DEV_RANDOM */
#ifndef IS_MODULES
static
#endif
const char * const gnupgext_version = "RNDLINUX ($Revision$)";
static struct {
int class;
int version;
void *func;
} func_table[] = {
{ 40, 1, gather_random },
{ 41, 1, fast_poll },
};
/****************
* The real random data collector for Unix.
* this function runs in a loop, waiting for commands from ctrl_fd
* and normally starts a collection process, which outputs random
* bytes to out_fd.
*
* Commands understand from ctrl_fd are single character:
* 'Q' = Quit the loop
* 'S' = Start a new collection process
* Enumerate the names of the functions together with informations about
* this function. Set sequence to an integer with a initial value of 0 and
* do not change it.
* If what is 0 all kind of functions are returned.
* Return values: class := class of function:
* 10 = message digest algorithm info function
* 11 = integer with available md algorithms
* 20 = cipher algorithm info function
* 21 = integer with available cipher algorithms
* 30 = public key algorithm info function
* 31 = integer with available pubkey algorithms
* 40 = get gather_random function
* 41 = get fast_random_poll function
* version = interface version of the function/pointer
* (currently this is 1 for all functions)
*/
static void
collector( FILE *ctrlfp, FILE *outfp )
#ifndef IS_MODULE
static
#endif
void *
gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
{
void *ret;
int i = *sequence;
do {
if ( i >= DIM(func_table) || i < 0 ) {
return NULL;
}
*class = func_table[i].class;
*vers = func_table[i].version;
ret = func_table[i].func;
i++;
} while ( what && what != *class );
*sequence = i;
return ret;
}
#endif /* no HAVE_DEV_RANDOM */
#endif /* USE_RAND_UNIX */
#ifndef IS_MODULE
void
rndlinux_constructor(void)
{
register_internal_cipher_extension( gnupgext_version,
gnupgext_enum_func );
}
#endif

754
cipher/rndunix.c Normal file
View File

@ -0,0 +1,754 @@
/****************************************************************************
* *
* BeOS Randomness-Gathering Code *
* Copyright Peter Gutmann, Paul Kendall, and Chris Wedgwood 1996-1998 *
* *
****************************************************************************/
/* General includes */
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_GETHRTIME
#include <sys/times.h>
#endif
#ifdef HAVE_GETTIMEOFDAY
#include <sys/times.h>
#endif
#ifdef HAVE_GETRUSAGE
#include <sys/resource.h>
#endif
/* OS-specific includes */
#ifdef __osf__
/* Somewhere in the morass of system-specific cruft which OSF/1 pulls in
* via the following includes are various endianness defines, so we
* undefine the cryptlib ones, which aren't really needed for this module
* anyway */
#undef BIG_ENDIAN
#undef LITTLE_ENDIAN
#endif /* __osf__ */
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#ifndef __QNX__
#include <sys/errno.h>
#include <sys/ipc.h>
#endif /* __QNX__ */
#include <sys/time.h> /* SCO and SunOS need this before resource.h */
#ifndef __QNX__
#include <sys/resource.h>
#endif /* __QNX__ */
#ifdef _AIX
#include <sys/select.h>
#endif /* _AIX */
#ifndef __QNX__
#include <sys/shm.h>
#include <sys/signal.h>
#endif /* __QNX__ */
#include <sys/stat.h>
#include <sys/types.h> /* Verschiedene komische Typen */
#if defined( __hpux ) && ( OS_VERSION == 9 )
#include <vfork.h>
#endif /* __hpux 9.x, after that it's in unistd.h */
#include <sys/wait.h>
/* #include <kitchensink.h> */
#include <errno.h>
#include "types.h" /* for byte and u32 typedefs */
#include "g10lib.h"
#ifndef IS_MODULE
#include "dynload.h"
#endif
typedef enum { FALSE=0, TRUE } BOOLEAN;
typedef unsigned char BYTE;
#define DEBUG_RANDOM 1
#define DEBUG_RANDOM_VERBOSE 1
/* The structure containing information on random-data sources. Each
* record contains the source and a relative estimate of its usefulness
* (weighting) which is used to scale the number of kB of output from the
* source (total = data_bytes / usefulness). Usually the weighting is in the
* range 1-3 (or 0 for especially useless sources), resulting in a usefulness
* rating of 1...3 for each kB of source output (or 0 for the useless
* sources).
*
* If the source is constantly changing (certain types of network statistics
* have this characteristic) but the amount of output is small, the weighting
* is given as a negative value to indicate that the output should be treated
* as if a minimum of 1K of output had been obtained. If the source produces
* a lot of output then the scale factor is fractional, resulting in a
* usefulness rating of < 1 for each kB of source output.
*
* In order to provide enough randomness to satisfy the requirements for a
* slow poll, we need to accumulate at least 20 points of usefulness (a
* typical system should get about 30 points).
*
* Some potential options are missed out because of special considerations.
* pstat -i and pstat -f can produce amazing amounts of output (the record
* is 600K on an Oracle server) which floods the buffer and doesn't yield
* anything useful (apart from perhaps increasing the entropy of the vmstat
* output a bit), so we don't bother with this. pstat in general produces
* quite a bit of output, but it doesn't change much over time, so it gets
* very low weightings. netstat -s produces constantly-changing output but
* also produces quite a bit of it, so it only gets a weighting of 2 rather
* than 3. The same holds for netstat -in, which gets 1 rather than 2.
*
* Some binaries are stored in different locations on different systems so
* alternative paths are given for them. The code sorts out which one to
* run by itself, once it finds an exectable somewhere it moves on to the
* next source. The sources are arranged roughly in their order of
* usefulness, occasionally sources which provide a tiny amount of
* relatively useless data are placed ahead of ones which provide a large
* amount of possibly useful data because another 100 bytes can't hurt, and
* it means the buffer won't be swamped by one or two high-output sources.
* All the high-output sources are clustered towards the end of the list
* for this reason. Some binaries are checked for in a certain order, for
* example under Slowaris /usr/ucb/ps understands aux as an arg, but the
* others don't. Some systems have conditional defines enabling alternatives
* to commands which don't understand the usual options but will provide
* enough output (in the form of error messages) to look like they're the
* real thing, causing alternative options to be skipped (we can't check the
* return either because some commands return peculiar, non-zero status even
* when they're working correctly).
*
* In order to maximise use of the buffer, the code performs a form of run-
* length compression on its input where a repeated sequence of bytes is
* replaced by the occurrence count mod 256. Some commands output an awful
* lot of whitespace, this measure greatly increases the amount of data we
* can fit in the buffer.
*
* When we scale the weighting using the SC() macro, some preprocessors may
* give a division by zero warning for the most obvious expression
* 'weight ? 1024 / weight : 0' (and gcc 2.7.2.2 dies with a division by zero
* trap), so we define a value SC_0 which evaluates to zero when fed to
* '1024 / SC_0' */
#define SC( weight ) ( 1024 / weight ) /* Scale factor */
#define SC_0 16384 /* SC( SC_0 ) evalutes to 0 */
static struct RI {
const char *path; /* Path to check for existence of source */
const char *arg; /* Args for source */
const int usefulness; /* Usefulness of source */
FILE *pipe; /* Pipe to source as FILE * */
int pipeFD; /* Pipe to source as FD */
pid_t pid; /* pid of child for waitpid() */
int length; /* Quantity of output produced */
const BOOLEAN hasAlternative; /* Whether source has alt.location */
} dataSources[] = {
{ "/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, FALSE},
{ "/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, FALSE},
{ "/usr/bin/pfstat", NULL, SC(-2), NULL, 0, 0, 0, FALSE},
{ "/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, FALSE},
{ "/usr/ucb/netstat", "-s", SC(2), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-s", SC(2), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-s", SC(2), NULL, 0, 0, 0, TRUE},
{ "/usr/etc/netstat", "-s", SC(2), NULL, 0, 0, 0, FALSE},
{ "/usr/bin/nfsstat", NULL, SC(2), NULL, 0, 0, 0, FALSE},
{ "/usr/ucb/netstat", "-m", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-m", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-m", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/etc/netstat", "-m", SC(-1), NULL, 0, 0, 0, FALSE },
{ "/bin/netstat", "-in", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/ucb/netstat", "-in", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-in", SC(-1), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-in", SC(-1), NULL, 0, 0, 0, TRUE},
{ "/usr/etc/netstat", "-in", SC(-1), NULL, 0, 0, 0, FALSE},
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.1.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* UDP in */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.4.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* UDP out */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.4.3.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* IP ? */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.10.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* TCP ? */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.11.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* TCP ? */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.13.0",
SC(-1), NULL, 0, 0, 0, FALSE }, /* TCP ? */
{ "/usr/bin/mpstat", NULL, SC(1), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/w", NULL, SC(1), NULL, 0, 0, 0, TRUE },
{ "/usr/bsd/w", NULL, SC(1), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/df", NULL, SC(1), NULL, 0, 0, 0, TRUE },
{ "/bin/df", NULL, SC(1), NULL, 0, 0, 0, FALSE },
{ "/usr/sbin/portstat", NULL, SC(1), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/iostat", NULL, SC(SC_0), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, TRUE },
{ "/usr/bsd/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, FALSE },
{ "/usr/ucb/netstat", "-n", SC(0.5), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, TRUE },
{ "/usr/etc/netstat", "-n", SC(0.5), NULL, 0, 0, 0, FALSE },
#if defined( __sgi ) || defined( __hpux )
{ "/bin/ps", "-el", SC(0.3), NULL, 0, 0, 0, TRUE },
#endif /* __sgi || __hpux */
{ "/usr/ucb/ps", "aux", SC(0.3), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, TRUE },
{ "/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, TRUE },
{ "/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, FALSE },
/* Unreliable source, depends on system usage */
{ "/etc/pstat", "-p", SC(0.5), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-p", SC(0.5), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-S", SC(0.2), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-S", SC(0.2), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-v", SC(0.2), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-v", SC(0.2), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-x", SC(0.2), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-x", SC(0.2), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-t", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-t", SC(0.1), NULL, 0, 0, 0, FALSE },
/* pstat is your friend */
{ "/usr/bin/last", "-n 50", SC(0.3), NULL, 0, 0, 0, TRUE },
#ifdef __sgi
{ "/usr/bsd/last", "-50", SC(0.3), NULL, 0, 0, 0, FALSE },
#endif /* __sgi */
#ifdef __hpux
{ "/etc/last", "-50", SC(0.3), NULL, 0, 0, 0, FALSE },
#endif /* __hpux */
{ "/usr/bsd/last", "-n 50", SC(0.3), NULL, 0, 0, 0, FALSE },
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.1.0",
SC(0.1), NULL, 0, 0, 0, FALSE }, /* ICMP ? */
{ "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.3.0",
SC(0.1), NULL, 0, 0, 0, FALSE }, /* ICMP ? */
{ "/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/usr/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/arp", "-a", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/arp", "-a", SC(0.1), NULL, 0, 0, 0, FALSE },
{ "/usr/sbin/ripquery", "-nw 1 127.0.0.1",
SC(0.1), NULL, 0, 0, 0, FALSE },
{ "/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, TRUE },
{ "/usr/ucb/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/tcpdump", "-c 5 -efvvx", SC(1), NULL, 0, 0, 0, FALSE },
/* This is very environment-dependant. If network traffic is low, it'll
* probably time out before delivering 5 packets, which is OK because
* it'll probably be fixed stuff like ARP anyway */
{ "/usr/sbin/advfsstat", "-b usr_domain",
SC(SC_0), NULL, 0, 0, 0, FALSE},
{ "/usr/sbin/advfsstat", "-l 2 usr_domain",
SC(0.5), NULL, 0, 0, 0, FALSE},
{ "/usr/sbin/advfsstat", "-p usr_domain",
SC(SC_0), NULL, 0, 0, 0, FALSE},
/* This is a complex and screwball program. Some systems have things
* like rX_dmn, x = integer, for RAID systems, but the statistics are
* pretty dodgy */
#if 0
/* The following aren't enabled since they're somewhat slow and not very
* unpredictable, however they give an indication of the sort of sources
* you can use (for example the finger might be more useful on a
* firewalled internal network) */
{ "/usr/bin/finger", "@ml.media.mit.edu", SC(0.9), NULL, 0, 0, 0, FALSE },
{ "/usr/local/bin/wget", "-O - http://lavarand.sgi.com/block.html",
SC(0.9), NULL, 0, 0, 0, FALSE },
{ "/bin/cat", "/usr/spool/mqueue/syslog", SC(0.9), NULL, 0, 0, 0, FALSE },
#endif /* 0 */
{ NULL, NULL, 0, NULL, 0, 0, 0, FALSE }
};
/* Variables to manage the child process which fills the buffer */
static pid_t gathererProcess = 0; /* The child process which fills the
* buffer */
static BYTE *gathererBuffer; /* Shared buffer for gathering random noise */
static int gathererMemID; /* ID for shared memory */
static int gathererBufSize; /* Size of the shared memory buffer */
static uid_t gathererID = (uid_t) - 1; /* Gatherers user ID */
/* The struct at the start of the shared memory buffer used to communicate
* information from the child to the parent */
typedef struct {
int usefulness; /* Usefulness of data in buffer */
int noBytes; /* No.of bytes in buffer */
} GATHERER_INFO;
/* Under SunOS popen() doesn't record the pid of the child process. When
* pclose() is called, instead of calling waitpid() for the correct child, it
* calls wait() repeatedly until the right child is reaped. The problem is
* that this reaps any other children that happen to have died at that
* moment, and when their pclose() comes along, the process hangs forever.
* The fix is to use a wrapper for popen()/pclose() which saves the pid in
* the dataSources structure (code adapted from GNU-libc's popen() call).
*
* Aut viam inveniam aut faciam */
static FILE *
my_popen(struct RI *entry)
{
int pipedes[2];
FILE *stream;
/* Create the pipe */
if (pipe(pipedes) < 0)
return (NULL);
/* Fork off the child ("vfork() is like an OS orgasm. All OS's want to
* do it, but most just end up faking it" - Chris Wedgwood). If your OS
* supports it, you should try to use vfork() here because it's somewhat
* more efficient */
#if defined( sun ) || defined( __ultrix__ ) || defined( __osf__ ) || \
defined(__hpux)
entry->pid = vfork();
#else /* */
entry->pid = fork();
#endif /* Unixen which have vfork() */
if (entry->pid == (pid_t) - 1) {
/* The fork failed */
close(pipedes[0]);
close(pipedes[1]);
return (NULL);
}
if (entry->pid == (pid_t) 0) {
struct passwd *passwd;
/* We are the child. Make the read side of the pipe be stdout */
if (dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
exit(127);
/* Now that everything is set up, give up our permissions to make
* sure we don't read anything sensitive. If the getpwnam() fails,
* we default to -1, which is usually nobody */
if (gathererID == (uid_t) - 1 && \
(passwd = getpwnam("nobody")) != NULL)
gathererID = passwd->pw_uid;
setuid(gathererID);
/* Close the pipe descriptors */
close(pipedes[STDIN_FILENO]);
close(pipedes[STDOUT_FILENO]);
/* Try and exec the program */
execl(entry->path, entry->path, entry->arg, NULL);
/* Die if the exec failed */
exit(127);
}
/* We are the parent. Close the irrelevant side of the pipe and open
* the relevant side as a new stream. Mark our side of the pipe to
* close on exec, so new children won't see it */
close(pipedes[STDOUT_FILENO]);
fcntl(pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
stream = fdopen(pipedes[STDIN_FILENO], "r");
if (stream == NULL) {
int savedErrno = errno;
/* The stream couldn't be opened or the child structure couldn't be
* allocated. Kill the child and close the other side of the pipe */
kill(entry->pid, SIGKILL);
if (stream == NULL)
close(pipedes[STDOUT_FILENO]);
else
fclose(stream);
waitpid(entry->pid, NULL, 0);
entry->pid = 0;
errno = savedErrno;
return (NULL);
}
return (stream);
}
static int
my_pclose(struct RI *entry)
{
int status = 0;
if (fclose(entry->pipe))
return (-1);
/* We ignore the return value from the process because some programs
* return funny values which would result in the input being discarded
* even if they executed successfully. This isn't a problem because the
* result data size threshold will filter out any programs which exit
* with a usage message without producing useful output */
if (waitpid(entry->pid, NULL, 0) != entry->pid)
status = -1;
entry->pipe = NULL;
entry->pid = 0;
return (status);
}
/* Unix slow poll (without special support for Linux)
*
* If a few of the randomness sources create a large amount of output then
* the slowPoll() stops once the buffer has been filled (but before all the
* randomness sources have been sucked dry) so that the 'usefulness' factor
* remains below the threshold. For this reason the gatherer buffer has to
* be fairly sizeable on moderately loaded systems. This is something of a
* bug since the usefulness should be influenced by the amount of output as
* well as the source type */
#define DEVRANDOM_BITS 1024
#define SHARED_BUFSIZE 49152 /* Usually about 25K are filled */
static void
slowPoll(void)
{
GATHERER_INFO *gathererInfo;
BOOLEAN moreSources;
struct timeval tv;
fd_set fds;
#if defined( __hpux )
size_t maxFD = 0;
int pageSize = 4096; /* PHUX doesn't have getpagesize() */
#elif defined( _M_XENIX ) || defined( __aux )
int maxFD = 0, pageSize = 4096; /* Nor do others, but they
* get fd right */
#else /* */
int maxFD = 0, pageSize = getpagesize();
#endif /* OS-specific brokenness */
int bufPos, i, usefulness = 0;
/* Make sure we don't start more than one slow poll at a time */
if (gathererProcess) {
g10_log_debug( "already in slowPoll\n");
return;
}
/* Set up the shared memory */
gathererBufSize = (SHARED_BUFSIZE / pageSize) * (pageSize + 1);
if ((gathererMemID = shmget(IPC_PRIVATE, gathererBufSize,
IPC_CREAT | 0600)) == -1) {
g10_log_debug("shmget failed: %s\n", strerror(errno) );
return; /* Something broke */
}
if ((gathererBuffer = (BYTE *) shmat(gathererMemID, NULL, 0)) == (BYTE *) - 1) {
g10_log_debug("shmat failed: %s\n", strerror(errno) );
return; /* Something broke */
}
/* Fork off the gatherer, the parent process returns to the caller */
if ((gathererProcess = fork()) || (gathererProcess == -1)) {
g10_log_debug("gatherer pid = %d\n", gathererProcess );
return; /* Error/parent process returns */
}
fclose(stderr); /* Arrghh!! It's Stuart code!! */
/* Reset the SIGC(H)LD handler to the system default. This is necessary
* because if the program which cryptlib is a part of installs its own
* SIGC(H)LD handler, it will end up reaping the cryptlib children before
* cryptlib can. As a result, my_pclose() will call waitpid() on a
* process which has already been reaped by the installed handler and
* return an error, so the read data won't be added to the randomness
* pool. There are two types of SIGC(H)LD naming, the SysV SIGCLD and
* the BSD/Posix SIGCHLD, so we need to handle either possibility */
#ifdef SIGCLD
signal(SIGCLD, SIG_DFL);
#else /* */
signal(SIGCHLD, SIG_DFL);
#endif /* SIGCLD */
/* Fire up each randomness source */
FD_ZERO(&fds);
for (i = 0; dataSources[i].path != NULL; i++) {
/* Since popen() is a fairly heavy function, we check to see whether
* the executable exists before we try to run it */
if (access(dataSources[i].path, X_OK)) {
#ifdef DEBUG_RANDOM_VERBOSE
printf("%s not present%s\n", dataSources[i].path,
dataSources[i].hasAlternative ? ", has alternatives" : "");
#endif /* DEBUG_RANDOM */
dataSources[i].pipe = NULL;
}
else
dataSources[i].pipe = my_popen(&dataSources[i]);
if (dataSources[i].pipe != NULL) {
dataSources[i].pipeFD = fileno(dataSources[i].pipe);
if (dataSources[i].pipeFD > maxFD)
maxFD = dataSources[i].pipeFD;
fcntl(dataSources[i].pipeFD, F_SETFL, O_NONBLOCK);
FD_SET(dataSources[i].pipeFD, &fds);
dataSources[i].length = 0;
/* If there are alternatives for this command, don't try and
* execute them */
while (dataSources[i].hasAlternative) {
#ifdef DEBUG_RANDOM_VERBOSE
printf("Skipping %s\n", dataSources[i + 1].path);
#endif /* DEBUG_RANDOM */
i++;
}
}
}
gathererInfo = (GATHERER_INFO *) gathererBuffer;
bufPos = sizeof(GATHERER_INFO); /* Start of buf.has status
* info */
/* Suck all the data we can get from each of the sources */
moreSources = TRUE;
while (moreSources && bufPos <= gathererBufSize) {
/* Wait for data to become available from any of the sources, with a
* timeout of 10 seconds. This adds even more randomness since data
* becomes available in a nondeterministic fashion. Kudos to HP's QA
* department for managing to ship a select() which breaks its own
* prototype */
tv.tv_sec = 10;
tv.tv_usec = 0;
#if defined( __hpux ) && ( OS_VERSION == 9 )
if (select(maxFD + 1, (int *)&fds, NULL, NULL, &tv) == -1)
#else /* */
if (select(maxFD + 1, &fds, NULL, NULL, &tv) == -1)
#endif /* __hpux */
break;
/* One of the sources has data available, read it into the buffer */
for (i = 0; dataSources[i].path != NULL; i++) {
if( dataSources[i].pipe && FD_ISSET(dataSources[i].pipeFD, &fds)) {
size_t noBytes;
if ((noBytes = fread(gathererBuffer + bufPos, 1,
gathererBufSize - bufPos,
dataSources[i].pipe)) == 0) {
if (my_pclose(&dataSources[i]) == 0) {
int total = 0;
/* Try and estimate how much entropy we're getting
* from a data source */
if (dataSources[i].usefulness)
if (dataSources[i].usefulness < 0)
total = (dataSources[i].length + 999)
/ -dataSources[i].usefulness;
else
total = dataSources[i].length
/ dataSources[i].usefulness;
#ifdef DEBUG_RANDOM
printf("%s %s contributed %d bytes (compressed), "
"usefulness = %d\n", dataSources[i].path,
(dataSources[i].arg != NULL) ?
dataSources[i].arg : "",
dataSources[i].length, total);
#endif /* DEBUG_RANDOM */
if( dataSources[i].length )
usefulness += total;
}
dataSources[i].pipe = NULL;
}
else {
int currPos = bufPos;
int endPos = bufPos + noBytes;
/* Run-length compress the input byte sequence */
while (currPos < endPos) {
int ch = gathererBuffer[currPos];
/* If it's a single byte, just copy it over */
if (ch != gathererBuffer[currPos + 1]) {
gathererBuffer[bufPos++] = ch;
currPos++;
}
else {
int count = 0;
/* It's a run of repeated bytes, replace them
* with the byte count mod 256 */
while ((ch == gathererBuffer[currPos])
&& currPos < endPos) {
count++;
currPos++;
}
gathererBuffer[bufPos++] = count;
noBytes -= count - 1;
}
}
/* Remember the number of (compressed) bytes of input we
* obtained */
dataSources[i].length += noBytes;
}
}
}
/* Check if there is more input available on any of the sources */
moreSources = FALSE;
FD_ZERO(&fds);
for (i = 0; dataSources[i].path != NULL; i++) {
if (dataSources[i].pipe != NULL) {
FD_SET(dataSources[i].pipeFD, &fds);
moreSources = TRUE;
}
}
}
gathererInfo->usefulness = usefulness;
gathererInfo->noBytes = bufPos;
#ifdef DEBUG_RANDOM
printf("Got %d bytes, usefulness = %d\n", bufPos, usefulness);
#endif /* DEBUG_RANDOM */
/* Child MUST exit here */
exit(0);
}
static void
fast_poll( void (*add)(const void*, size_t, int) )
{
#if HAVE_GETHRTIME
{ hrtime_t tv;
tv = gethrtime();
(*add)( &tv, sizeof(tv), 1 );
}
#elif HAVE_GETTIMEOFDAY
{ struct timeval tv;
if( gettimeofday( &tv, NULL ) )
BUG();
(*add)( &tv.tv_sec, sizeof(tv.tv_sec), 1 );
(*add)( &tv.tv_usec, sizeof(tv.tv_usec), 1 );
}
#else /* use times */
{ struct tms buf;
times( &buf );
(*add)( &buf, sizeof buf, 1 );
}
#endif
#ifdef HAVE_GETRUSAGE
{ struct rusage buf;
if( getrusage( RUSAGE_SELF, &buf ) )
BUG();
(*add)( &buf, sizeof buf, 1 );
memset( &buf, 0, sizeof buf );
}
#endif
}
static int
gather_random( byte *buffer, size_t *r_length, int level )
{
GATHERER_INFO gathererInfo;
int status;
size_t n;
size_t length = *r_length;
slowPoll();
assert( gathererProcess );
/* Wait for the gathering process to finish, add the randomness it's
* gathered, and detach the shared memory */
waitpid(gathererProcess, &status, 0); /* Should prob.check status */
gathererInfo = *(GATHERER_INFO *)gathererBuffer;
n = gathererInfo.noBytes;
if( n > length )
n = length;
memcpy( buffer, gathererBuffer, n );
memset(gathererBuffer, 0, gathererBufSize);
shmdt(gathererBuffer);
shmctl(gathererMemID, IPC_RMID, NULL);
gathererProcess = 0;
*r_length = n;
if( gathererInfo.usefulness > 30 )
return 100;
else if ( gathererInfo.usefulness )
return gathererInfo.usefulness * 100 / 30;
else
return 0;
}
#ifndef IS_MODULE
static
#endif
const char * const gnupgext_version = "RNDUNIX ($Revision$)";
static struct {
int class;
int version;
void *func;
} func_table[] = {
{ 40, 1, gather_random },
{ 41, 1, fast_poll },
};
/****************
* Enumerate the names of the functions together with informations about
* this function. Set sequence to an integer with a initial value of 0 and
* do not change it.
* If what is 0 all kind of functions are returned.
* Return values: class := class of function:
* 10 = message digest algorithm info function
* 11 = integer with available md algorithms
* 20 = cipher algorithm info function
* 21 = integer with available cipher algorithms
* 30 = public key algorithm info function
* 31 = integer with available pubkey algorithms
* 40 = get read_random_source() function
* 41 = get fast_random_poll function
* version = interface version of the function/pointer
* (currently this is 1 for all functions)
*/
#ifndef IS_MODULE
static
#endif
void *
gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
{
void *ret;
int i = *sequence;
do {
if ( i >= DIM(func_table) || i < 0 ) {
return NULL;
}
*class = func_table[i].class;
*vers = func_table[i].version;
ret = func_table[i].func;
i++;
} while ( what && what != *class );
*sequence = i;
return ret;
}
#ifndef IS_MODULE
void
rndunix_constructor(void)
{
register_internal_cipher_extension( gnupgext_version,
gnupgext_enum_func );
}
#endif