diff --git a/cipher/ChangeLog b/cipher/ChangeLog index b6072a80e..415f58b2e 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -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) diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 1b96cb0b5..69ba2962e 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -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 \ diff --git a/cipher/dynload.c b/cipher/dynload.c index ff40dafff..7278928f1 100644 --- a/cipher/dynload.c +++ b/cipher/dynload.c @@ -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; +} + diff --git a/cipher/dynload.h b/cipher/dynload.h index ad22a824f..5e88dc732 100644 --- a/cipher/dynload.h +++ b/cipher/dynload.h @@ -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*/ diff --git a/cipher/rand-dummy.c b/cipher/rand-dummy.c deleted file mode 100644 index 093108b06..000000000 --- a/cipher/rand-dummy.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_GETHRTIME - #include -#endif -#ifdef HAVE_GETTIMEOFDAY - #include -#endif -#ifdef HAVE_GETRUSAGE - #include -#endif -#include -#include -#ifdef __MINGW32__ - #include -#endif -#include -#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 */ diff --git a/cipher/rand-internal.h b/cipher/rand-internal.h index e1e137871..230ea975e 100644 --- a/cipher/rand-internal.h +++ b/cipher/rand-internal.h @@ -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*/ diff --git a/cipher/rand-w32.c b/cipher/rand-w32.c deleted file mode 100644 index c96e04eba..000000000 --- a/cipher/rand-w32.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 */ diff --git a/cipher/random.c b/cipher/random.c index e22f3d70c..e173a5279 100644 --- a/cipher/random.c +++ b/cipher/random.c @@ -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 */ + diff --git a/cipher/random.h b/cipher/random.h index 2ac50a7d4..d4c134275 100644 --- a/cipher/random.h +++ b/cipher/random.h @@ -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*/ diff --git a/cipher/rand-unix.c b/cipher/rndlinux.c similarity index 58% rename from cipher/rand-unix.c rename to cipher/rndlinux.c index d256fd20b..de2710ce5 100644 --- a/cipher/rand-unix.c +++ b/cipher/rndlinux.c @@ -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 #include #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 + diff --git a/cipher/rndunix.c b/cipher/rndunix.c new file mode 100644 index 000000000..530971723 --- /dev/null +++ b/cipher/rndunix.c @@ -0,0 +1,754 @@ +/**************************************************************************** + * * + * BeOS Randomness-Gathering Code * + * Copyright Peter Gutmann, Paul Kendall, and Chris Wedgwood 1996-1998 * + * * + ****************************************************************************/ + +/* General includes */ + +#include +#include +#include +#include +#include +#ifdef HAVE_GETHRTIME + #include +#endif +#ifdef HAVE_GETTIMEOFDAY + #include +#endif +#ifdef HAVE_GETRUSAGE + #include +#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 +#include +#include +#ifndef __QNX__ +#include +#include +#endif /* __QNX__ */ +#include /* SCO and SunOS need this before resource.h */ +#ifndef __QNX__ +#include +#endif /* __QNX__ */ +#ifdef _AIX +#include +#endif /* _AIX */ +#ifndef __QNX__ +#include +#include +#endif /* __QNX__ */ +#include +#include /* Verschiedene komische Typen */ +#if defined( __hpux ) && ( OS_VERSION == 9 ) +#include +#endif /* __hpux 9.x, after that it's in unistd.h */ +#include +/* #include */ +#include + +#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 + +