aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>1998-11-25 11:52:41 +0000
committerWerner Koch <[email protected]>1998-11-25 11:52:41 +0000
commit2e494682b63e9f7abc2a34711618f4ebd21e2744 (patch)
treee8afb0e10b4657a3d841ff9cb02a752698c19f97
parentReady for version 0.4.4 (diff)
downloadgnupg-2e494682b63e9f7abc2a34711618f4ebd21e2744.tar.gz
gnupg-2e494682b63e9f7abc2a34711618f4ebd21e2744.zip
Restructured the RNG source and add support for loadable
random modules.
-rw-r--r--cipher/ChangeLog15
-rw-r--r--cipher/Makefile.am13
-rw-r--r--cipher/dynload.c93
-rw-r--r--cipher/dynload.h10
-rw-r--r--cipher/rand-dummy.c134
-rw-r--r--cipher/rand-internal.h35
-rw-r--r--cipher/rand-w32.c54
-rw-r--r--cipher/random.c143
-rw-r--r--cipher/random.h9
-rw-r--r--cipher/rndlinux.c (renamed from cipher/rand-unix.c)118
-rw-r--r--cipher/rndunix.c754
11 files changed, 1099 insertions, 279 deletions
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 ([email protected])
+
+ * 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 ([email protected])
- * 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 <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 */
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 <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 */
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
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 <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 */
-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);
-}
+static int open_device( const char *name, int minor );
+static int gather_random( byte *buffer, size_t *r_length, int level );
-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;
+}
+#ifndef IS_MODULE
+void
+rndlinux_constructor(void)
+{
+ register_internal_cipher_extension( gnupgext_version,
+ gnupgext_enum_func );
}
+#endif
-#endif /* no HAVE_DEV_RANDOM */
-#endif /* USE_RAND_UNIX */
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 <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
+
+