Restructured the RNG source and add support for loadable
authorWerner Koch <wk@gnupg.org>
Wed, 25 Nov 1998 11:52:41 +0000 (11:52 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 25 Nov 1998 11:52:41 +0000 (11:52 +0000)
random modules.

cipher/ChangeLog
cipher/Makefile.am
cipher/dynload.c
cipher/dynload.h
cipher/rand-dummy.c [deleted file]
cipher/rand-internal.h
cipher/rand-w32.c [deleted file]
cipher/random.c
cipher/random.h
cipher/rndlinux.c [moved from cipher/rand-unix.c with 58% similarity]
cipher/rndunix.c [new file with mode: 0644]

index b6072a8..415f58b 100644 (file)
@@ -1,6 +1,19 @@
+Wed Nov 25 12:33:41 1998  Werner Koch  (wk@isil.d.shuttle.de)
+
+       * rand-*.c: Removed.
+       * rndlinux.c : New.
+       * rndunix.c : New.
+       * random.c : Restructured the interface to the gather modules.
+       (intialize): Call constructor functions
+       (read_radnom_source): Moved to here.
+       * dynload.c (dynload_getfnc_gather_random): New.
+       (dynload_getfnc_fast_random_poll): New.
+       (register_internal_cipher_extension): New.
+       (register_cipher_extension): Support of internal modules.
+
 Sun Nov  8 17:44:36 1998  Werner Koch  (wk@isil.d.shuttle.de)
 
-       * radn-unix.c (read_random_source): Removed the assert.
+       * rand-unix.c (read_random_source): Removed the assert.
 
 Mon Oct 19 18:34:30 1998  me,,,  (wk@tobold)
 
index 1b96cb0..69ba296 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-gnupg_extensions = tiger twofish
+gnupg_extensions = tiger twofish rndunix
 
 INCLUDES =  -I$(top_srcdir)/include -I$(top_srcdir)/intl
 
@@ -11,7 +11,7 @@ else
 pkglib_PROGRAMS  =
 endif
 
-DYNLINK_MOD_CFLAGS = @DYNLINK_MOD_CFLAGS@
+DYNLINK_MOD_CFLAGS = -DIS_MODULE @DYNLINK_MOD_CFLAGS@
 
 
 libcipher_a_SOURCES = cipher.c \
@@ -33,9 +33,7 @@ libcipher_a_SOURCES = cipher.c        \
                 random.h       \
                 random.c       \
                 rand-internal.h \
-                rand-unix.c    \
-                rand-w32.c     \
-                rand-dummy.c   \
+                rndlinux.c     \
                 rmd.h          \
                 rmd160.c       \
                 sha1.h         \
@@ -58,6 +56,11 @@ twofish: $(srcdir)/twofish.c
            sed -e 's/-O[0-9]*/  /' `
 
 
+rndunix: $(srcdir)/rndunix.c
+       $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o rndunix $(srcdir)/rndunix.c
+
+
+
 install-exec-hook:
        @list='$(pkglib_PROGRAMS)'; for p in $$list; do \
          if test -f $(pkglibdir)/$$p; then \
index ff40daf..7278928 100644 (file)
@@ -39,6 +39,7 @@
 
 typedef struct ext_list {
     struct ext_list *next;
+    int internal;
   #ifdef HAVE_DL_DLOPEN
     void *handle; /* handle from dlopen() */
   #else
@@ -83,7 +84,7 @@ static int dld_available;
 void
 register_cipher_extension( const char *mainpgm, const char *fname )
 {
-    EXTLIST r, el;
+    EXTLIST r, el, intex;
     char *p, *pe;
 
   #ifdef HAVE_DLD_DLD_LINK
@@ -114,13 +115,53 @@ register_cipher_extension( const char *mainpgm, const char *fname )
        el->hintstr = NULL;
 
     /* check that it is not already registered */
-    for(r = extensions; r; r = r->next )
+    intex = NULL;
+    for(r = extensions; r; r = r->next ) {
+       if( !compare_filenames(r->name, el->name) ) {
+           log_info("extension '%s' already registered\n", el->name );
+           m_free(el);
+           return;
+       }
+       else if( r->internal )
+           intex = r;
+    }
+    /* and register */
+    /* we put them after the internal extension modules */
+    /* this is so that the external modules do not get loaded */
+    /* as soon as the internal modules are requested */
+    if( intex ) {
+       el->next = intex->next;
+       intex->next = el;
+    }
+    else {
+       el->next = extensions;
+       extensions = el;
+    }
+}
+
+void
+register_internal_cipher_extension(
+                       const char *module_id,
+                       void * (*enumfunc)(int, int*, int*, int*)
+                                 )
+{
+    EXTLIST r, el;
+
+    el = m_alloc_clear( sizeof *el + strlen(module_id) );
+    strcpy(el->name, module_id );
+    el->internal = 1;
+
+    /* check that it is not already registered */
+    for(r = extensions; r; r = r->next ) {
        if( !compare_filenames(r->name, el->name) ) {
            log_info("extension '%s' already registered\n", el->name );
            m_free(el);
            return;
        }
+    }
     /* and register */
+    el->enumfunc = enumfunc;
+    el->handle = (void*)1;
     el->next = extensions;
     extensions = el;
 }
@@ -455,3 +496,51 @@ enum_gnupgext_pubkeys( void **enum_context, int *algo,
     return NULL;
 }
 
+
+int (*
+dynload_getfnc_gather_random())(byte*, size_t*, int)
+{
+    EXTLIST r;
+    void *sym;
+
+    for( r = extensions; r; r = r->next )  {
+       int seq, class, vers;
+
+       if( r->failed )
+           continue;
+       if( !r->handle && load_extension(r) )
+           continue;
+       seq = 0;
+       while( (sym = (*r->enumfunc)(40, &seq, &class, &vers)) ) {
+           if( vers != 1 || class != 40 )
+               continue;
+           return (int (*)(byte*, size_t*, int))sym;
+       }
+    }
+    return NULL;
+}
+
+
+void (*
+dynload_getfnc_fast_random_poll())( void (*)(const void*, size_t, int))
+{
+    EXTLIST r;
+    void *sym;
+
+    for( r = extensions; r; r = r->next )  {
+       int seq, class, vers;
+
+       if( r->failed )
+           continue;
+       if( !r->handle && load_extension(r) )
+           continue;
+       seq = 0;
+       while( (sym = (*r->enumfunc)(41, &seq, &class, &vers)) ) {
+           if( vers != 1 || class != 41 )
+               continue;
+           return (void (*)( void (*)(const void*, size_t, int)))sym;
+       }
+    }
+    return NULL;
+}
+
index ad22a82..5e88dc7 100644 (file)
 #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 (file)
index 093108b..0000000
+++ /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 */
index e1e1378..230ea97 100644 (file)
 #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 (file)
index c96e04e..0000000
+++ /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 */
index e22f3d7..e173a52 100644 (file)
@@ -36,7 +36,9 @@
 #include "rmd.h"
 #include "ttyio.h"
 #include "i18n.h"
+#include "random.h"
 #include "rand-internal.h"
+#include "dynload.h"
 
 
 #if SIZEOF_UNSIGNED_LONG == 8
   #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 */
+
index 2ac50a7..d4c1342 100644 (file)
@@ -27,13 +27,6 @@ void secure_random_alloc(void);
 int  quick_random_gen( int onoff );
 void randomize_buffer( byte *buffer, size_t length, int level );
 byte *get_random_bits( size_t nbits, int level, int secure );
-void add_randomness( const void *buffer, size_t length, int source );
-
-
-/*-- the next two functions are implemented by all the system
-     specific source files rand-xxxx.c --*/
-void random_poll(void);
-void fast_random_poll(void);
-
+void fast_random_poll( void );
 
 #endif /*G10_RANDOM_H*/
similarity index 58%
rename from cipher/rand-unix.c
rename to cipher/rndlinux.c
index d256fd2..de2710c 100644 (file)
@@ -1,14 +1,14 @@
-/* rand-unix.c -  raw random number generator for unix like OSes
+/* rndlinux.c  -  raw random number for OSes with /dev/random
  *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #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 (file)
index 0000000..5309717
--- /dev/null
@@ -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
+
+