See ChangeLog: Wed Dec 8 21:58:32 CET 1999 Werner Koch
[gnupg.git] / cipher / rndunix.c
index 5309717..4ab9f65 100644 (file)
@@ -1,10 +1,52 @@
 /****************************************************************************
  *                                                                         *
- *   BeOS Randomness-Gathering Code                                        *
- *   Copyright Peter Gutmann, Paul Kendall, and Chris Wedgwood 1996-1998    *
+ *                                                                         *
+ *   Unix Randomness-Gathering Code                                        *
+ *                                                                         *
+ *   Copyright Peter Gutmann, Paul Kendall, and Chris Wedgwood 1996-1999.   *
+ *   Heavily modified for GnuPG by Werner Koch                             *
+ *                                                                         *
  *                                                                         *
  ****************************************************************************/
 
+/* This module is part of the cryptlib continuously seeded pseudorandom
+   number generator.  For usage conditions, see lib_rand.c
+
+   [Here is the notice from lib_rand.c:]
+
+   This module and the misc/rnd*.c modules represent the cryptlib
+   continuously seeded pseudorandom number generator (CSPRNG) as described in
+   my 1998 Usenix Security Symposium paper "The generation of random numbers
+   for cryptographic purposes".
+
+   The CSPRNG code is copyright Peter Gutmann (and various others) 1996,
+   1997, 1998, 1999, all rights reserved.  Redistribution of the CSPRNG
+   modules and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice
+      and this permission notice in its entirety.
+
+   2. Redistributions in binary form must reproduce the copyright notice in
+      the documentation and/or other materials provided with the distribution.
+
+   3. A copy of any bugfixes or enhancements made must be provided to the
+      author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
+      baseline version of the code.
+
+  ALTERNATIVELY, the code may be distributed under the terms of the GNU
+  General Public License, version 2 or any later version published by the
+  Free Software Foundation, in which case the provisions of the GNU GPL are
+  required INSTEAD OF the above restrictions.
+
+  Although not required under the terms of the GPL, it would still be nice if
+  you could make any changes available to the author to allow a consistent
+  code base to be maintained */
+
+
+/* Fixme: We use plain mallocs here beucase it may be used as a module
+ * should be changed. */
+
 /* General includes */
 
 #include <config.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 */
 
 #include <errno.h>
 
 #include "types.h"  /* for byte and u32 typedefs */
-#include "g10lib.h"
 #ifndef IS_MODULE
 #include "dynload.h"
 #endif
+#include "g10lib.h"
 
-typedef enum { FALSE=0, TRUE } BOOLEAN;
-typedef unsigned char BYTE;
+#ifndef EAGAIN
+  #define EAGAIN  EWOULDBLOCK
+#endif
 
-#define DEBUG_RANDOM  1
-#define DEBUG_RANDOM_VERBOSE  1
+#define GATHER_BUFSIZE         49152   /* Usually about 25K are filled */
 
 /* The structure containing information on random-data sources.  Each
  * record contains the source and a relative estimate of its usefulness
@@ -142,110 +175,110 @@ static struct RI {
     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 */
+    const int 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},
+    {  "/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, 1    },
+    {  "/usr/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, 0},
+    {  "/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, 1     },
+    {  "/usr/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, 0},
+    {  "/usr/bin/pfstat", NULL, SC(-2), NULL, 0, 0, 0, 0},
+    {  "/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, 1     },
+    {  "/usr/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, 0},
+    {  "/usr/ucb/netstat", "-s", SC(2), NULL, 0, 0, 0, 1 },
+    {  "/usr/bin/netstat", "-s", SC(2), NULL, 0, 0, 0, 1 },
+    {  "/usr/sbin/netstat", "-s", SC(2), NULL, 0, 0, 0, 1},
+    {  "/usr/etc/netstat", "-s", SC(2), NULL, 0, 0, 0, 0},
+    {  "/usr/bin/nfsstat", NULL, SC(2), NULL, 0, 0, 0, 0},
+    {  "/usr/ucb/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1  },
+    {  "/usr/bin/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1  },
+    {  "/usr/sbin/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1 },
+    {  "/usr/etc/netstat", "-m", SC(-1), NULL, 0, 0, 0, 0 },
+    {  "/bin/netstat",     "-in", SC(-1), NULL, 0, 0, 0, 1 },
+    {  "/usr/ucb/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1 },
+    {  "/usr/bin/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1 },
+    {  "/usr/sbin/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1},
+    {  "/usr/etc/netstat", "-in", SC(-1), NULL, 0, 0, 0, 0},
     {  "/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 */
+                                   SC(-1), NULL, 0, 0, 0, 0 }, /* 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 */
+                                   SC(-1), NULL, 0, 0, 0, 0 },  /* 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 ? */
+                                   SC(-1), NULL, 0, 0, 0, 0 }, /* 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 ? */
+                                   SC(-1), NULL, 0, 0, 0, 0 }, /* 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 ? */
+                                   SC(-1), NULL, 0, 0, 0, 0 }, /* 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  },
+                                   SC(-1), NULL, 0, 0, 0, 0 }, /* TCP ? */
+    {  "/usr/bin/mpstat", NULL, SC(1), NULL, 0, 0, 0, 0     },
+    {  "/usr/bin/w", NULL, SC(1), NULL, 0, 0, 0, 1           },
+    {  "/usr/bsd/w", NULL, SC(1), NULL, 0, 0, 0, 0          },
+    {  "/usr/bin/df", NULL, SC(1), NULL, 0, 0, 0, 1          },
+    {  "/bin/df", NULL, SC(1), NULL, 0, 0, 0, 0             },
+    {  "/usr/sbin/portstat", NULL, SC(1), NULL, 0, 0, 0, 0  },
+    {  "/usr/bin/iostat", NULL, SC(SC_0), NULL, 0, 0, 0, 0  },
+    {  "/usr/bin/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, 1   },
+    {  "/usr/bsd/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, 0  },
+    {  "/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, 1       },
+    {  "/usr/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, 0  },
+    {  "/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, 1       },
+    {  "/usr/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, 0  },
+    {  "/usr/ucb/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1   },
+    {  "/usr/bin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1   },
+    {  "/usr/sbin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1  },
+    {  "/usr/etc/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 0  },
 #if defined( __sgi ) || defined( __hpux )
-    {  "/bin/ps", "-el", SC(0.3), NULL, 0, 0, 0, TRUE           },
+    {  "/bin/ps", "-el", SC(0.3), NULL, 0, 0, 0, 1           },
 #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         },
+    {  "/usr/ucb/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1       },
+    {  "/usr/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1       },
+    {  "/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 0          },
+    {  "/usr/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 1      },
+    {  "/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 0         },
     /* 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        },
+    {  "/etc/pstat", "-p", SC(0.5), NULL, 0, 0, 0, 1         },
+    {  "/bin/pstat", "-p", SC(0.5), NULL, 0, 0, 0, 0        },
+    {  "/etc/pstat", "-S", SC(0.2), NULL, 0, 0, 0, 1         },
+    {  "/bin/pstat", "-S", SC(0.2), NULL, 0, 0, 0, 0        },
+    {  "/etc/pstat", "-v", SC(0.2), NULL, 0, 0, 0, 1         },
+    {  "/bin/pstat", "-v", SC(0.2), NULL, 0, 0, 0, 0        },
+    {  "/etc/pstat", "-x", SC(0.2), NULL, 0, 0, 0, 1         },
+    {  "/bin/pstat", "-x", SC(0.2), NULL, 0, 0, 0, 0        },
+    {  "/etc/pstat", "-t", SC(0.1), NULL, 0, 0, 0, 1         },
+    {  "/bin/pstat", "-t", SC(0.1), NULL, 0, 0, 0, 0        },
     /* pstat is your friend */
-    {  "/usr/bin/last", "-n 50", SC(0.3), NULL, 0, 0, 0, TRUE   },
+    {  "/usr/bin/last", "-n 50", SC(0.3), NULL, 0, 0, 0, 1   },
 #ifdef __sgi
-    {  "/usr/bsd/last", "-50", SC(0.3), NULL, 0, 0, 0, FALSE    },
+    {  "/usr/bsd/last", "-50", SC(0.3), NULL, 0, 0, 0, 0    },
 #endif                         /* __sgi */
 #ifdef __hpux
-    {  "/etc/last", "-50", SC(0.3), NULL, 0, 0, 0, FALSE        },
+    {  "/etc/last", "-50", SC(0.3), NULL, 0, 0, 0, 0        },
 #endif                         /* __hpux */
-    {  "/usr/bsd/last", "-n 50", SC(0.3), NULL, 0, 0, 0, FALSE  },
+    {  "/usr/bsd/last", "-n 50", SC(0.3), NULL, 0, 0, 0, 0  },
     {  "/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 ? */
+                               SC(0.1), NULL, 0, 0, 0, 0 }, /* 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 },
+                               SC(0.1), NULL, 0, 0, 0, 0 }, /* ICMP ? */
+    {  "/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1  },
+    {  "/usr/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1  },
+    {  "/usr/bin/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1  },
+    {  "/usr/sbin/arp", "-a", SC(0.1), NULL, 0, 0, 0, 0 },
     {  "/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 },
+                               SC(0.1), NULL, 0, 0, 0, 0 },
+    {  "/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 1     },
+    {  "/usr/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 1 },
+    {  "/usr/ucb/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 0 },
+    {  "/usr/bin/tcpdump", "-c 5 -efvvx", SC(1), NULL, 0, 0, 0, 0 },
     /* 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},
+                               SC(SC_0), NULL, 0, 0, 0, 0},
     {  "/usr/sbin/advfsstat", "-l 2 usr_domain",
-                               SC(0.5), NULL, 0, 0, 0, FALSE},
+                               SC(0.5), NULL, 0, 0, 0, 0},
     {  "/usr/sbin/advfsstat", "-p usr_domain",
-                               SC(SC_0), NULL, 0, 0, 0, FALSE},
+                               SC(SC_0), NULL, 0, 0, 0, 0},
     /* 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 */
@@ -254,30 +287,24 @@ static struct RI {
      * 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/bin/finger", "@ml.media.mit.edu", SC(0.9), NULL, 0, 0, 0, 0 },
     {  "/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 },
+                               SC(0.9), NULL, 0, 0, 0, 0 },
+    {  "/bin/cat", "/usr/spool/mqueue/syslog", SC(0.9), NULL, 0, 0, 0, 0 },
 #endif                         /* 0 */
-    {  NULL, NULL, 0, NULL, 0, 0, 0, FALSE }
+    {  NULL, NULL, 0, NULL, 0, 0, 0, 0 }
 };
 
-/* 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 */
+static byte *gather_buffer;        /* buffer for gathering random noise */
+static int gather_buffer_size;     /* size of the memory buffer */
+static uid_t gatherer_uid;
 
+/* The message structure used to communicate with the parent */
 typedef struct {
-    int usefulness;            /* Usefulness of data in buffer */
-    int noBytes;               /* No.of bytes in buffer */
-} GATHERER_INFO;
+    int  usefulness;   /* usefulness of data */
+    int  ndata;        /* valid bytes in data */
+    char data[500];    /* gathered data */
+} GATHER_MSG;
 
 /* 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
@@ -327,11 +354,11 @@ my_popen(struct RI *entry)
        /* 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 && \
+       if (gatherer_uid == (uid_t)-1 && \
            (passwd = getpwnam("nobody")) != NULL)
-           gathererID = passwd->pw_uid;
+           gatherer_uid = passwd->pw_uid;
 
-       setuid(gathererID);
+       setuid(gatherer_uid);
 
        /* Close the pipe descriptors */
        close(pipedes[STDIN_FILENO]);
@@ -406,70 +433,20 @@ my_pclose(struct RI *entry)
  * 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)
+static int
+slow_poll(FILE *dbgfp, int dbgall, size_t *nbytes )
 {
-    GATHERER_INFO *gathererInfo;
-    BOOLEAN moreSources;
+    int moreSources;
     struct timeval tv;
     fd_set fds;
-#if defined( __hpux )
+  #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 */
+  #else
+    int maxFD = 0;
+  #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);
@@ -477,10 +454,10 @@ slowPoll(void)
        /* 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 */
+           if( dbgfp && dbgall )
+               fprintf(dbgfp, "%s not present%s\n", dataSources[i].path,
+                              dataSources[i].hasAlternative ?
+                                       ", has alternatives" : "");
            dataSources[i].pipe = NULL;
        }
        else
@@ -490,28 +467,29 @@ slowPoll(void)
            dataSources[i].pipeFD = fileno(dataSources[i].pipe);
            if (dataSources[i].pipeFD > maxFD)
                maxFD = dataSources[i].pipeFD;
+         #ifdef O_NONBLOCK /* Ohhh what a hack (used for Atari) */
            fcntl(dataSources[i].pipeFD, F_SETFL, O_NONBLOCK);
+         #else
+           #warning O_NONBLOCK is missing
+         #endif
            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 */
+               if( dbgfp && dbgall )
+                   fprintf(dbgfp, "Skipping %s\n", dataSources[i + 1].path);
                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) {
+    bufPos = 0;
+    moreSources = 1;
+    while (moreSources && bufPos <= gather_buffer_size) {
        /* 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
@@ -520,11 +498,11 @@ slowPoll(void)
        tv.tv_sec = 10;
        tv.tv_usec = 0;
 
-#if defined( __hpux ) && ( OS_VERSION == 9 )
+      #if defined( __hpux ) && ( OS_VERSION == 9 )
        if (select(maxFD + 1, (int *)&fds, NULL, NULL, &tv) == -1)
-#else                          /*  */
+      #else  /*  */
        if (select(maxFD + 1, &fds, NULL, NULL, &tv) == -1)
-#endif                         /* __hpux */
+      #endif /* __hpux */
            break;
 
        /* One of the sources has data available, read it into the buffer */
@@ -532,28 +510,29 @@ slowPoll(void)
            if( dataSources[i].pipe && FD_ISSET(dataSources[i].pipeFD, &fds)) {
                size_t noBytes;
 
-               if ((noBytes = fread(gathererBuffer + bufPos, 1,
-                                    gathererBufSize - bufPos,
+               if ((noBytes = fread(gather_buffer + bufPos, 1,
+                                    gather_buffer_size - 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) {
                            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), "
+                       }
+                       if( dbgfp )
+                           fprintf(dbgfp,
+                              "%s %s contributed %d bytes, "
                               "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;
                    }
@@ -565,11 +544,11 @@ slowPoll(void)
 
                    /* Run-length compress the input byte sequence */
                    while (currPos < endPos) {
-                       int ch = gathererBuffer[currPos];
+                       int ch = gather_buffer[currPos];
 
                        /* If it's a single byte, just copy it over */
-                       if (ch != gathererBuffer[currPos + 1]) {
-                           gathererBuffer[bufPos++] = ch;
+                       if (ch != gather_buffer[currPos + 1]) {
+                           gather_buffer[bufPos++] = ch;
                            currPos++;
                        }
                        else {
@@ -577,12 +556,12 @@ slowPoll(void)
 
                            /* It's a run of repeated bytes, replace them
                             * with the byte count mod 256 */
-                           while ((ch == gathererBuffer[currPos])
+                           while ((ch == gather_buffer[currPos])
                                    && currPos < endPos) {
                                count++;
                                currPos++;
                            }
-                           gathererBuffer[bufPos++] = count;
+                           gather_buffer[bufPos++] = count;
                            noBytes -= count - 1;
                        }
                    }
@@ -595,93 +574,223 @@ slowPoll(void)
        }
 
        /* Check if there is more input available on any of the sources */
-       moreSources = FALSE;
+       moreSources = 0;
        FD_ZERO(&fds);
        for (i = 0; dataSources[i].path != NULL; i++) {
            if (dataSources[i].pipe != NULL) {
                FD_SET(dataSources[i].pipeFD, &fds);
-               moreSources = TRUE;
+               moreSources = 1;
            }
        }
     }
 
-    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);
+    if( dbgfp ) {
+       fprintf(dbgfp, "Got %d bytes, usefulness = %d\n", bufPos, usefulness);
+       fflush(dbgfp);
+    }
+    *nbytes = bufPos;
+    return usefulness;
 }
 
-
+/****************
+ * Start the gatherer process which writes messages of
+ * type GATHERER_MSG to pipedes
+ */
 static void
-fast_poll( void (*add)(const void*, size_t, int) )
+start_gatherer( int pipefd )
 {
-  #if HAVE_GETHRTIME
-    {  hrtime_t tv;
-       tv = gethrtime();
-       (*add)( &tv, sizeof(tv), 1 );
+    FILE *dbgfp = NULL;
+    int dbgall;
+
+    {
+       const char *s = getenv("GNUPG_RNDUNIX_DBG");
+       if( s ) {
+           dbgfp = (*s=='-' && !s[1])? stdout : fopen(s, "a");
+           if( !dbgfp )
+               g10_log_info("can't open debug file `%s': %s\n",
+                            s, strerror(errno) );
+           else
+               fprintf(dbgfp,"\nSTART RNDUNIX DEBUG pid=%d\n", (int)getpid());
+       }
+       dbgall = !!getenv("GNUPG_RNDUNIX_DBGALL");
     }
-  #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 );
+    /* close all files but the ones we need */
+    {  int nmax, n1, n2, i;
+       if( (nmax=sysconf( _SC_OPEN_MAX )) < 0 ) {
+         #ifdef _POSIX_OPEN_MAX
+           nmax = _POSIX_OPEN_MAX;
+         #else
+           nmax = 20; /* assume a reasonable value */
+         #endif
+       }
+       n1 = fileno( stderr );
+       n2 = dbgfp? fileno( dbgfp ) : -1;
+       for(i=0; i < nmax; i++ ) {
+           if( i != n1 && i != n2 && i != pipefd )
+               close(i);
+       }
+       errno = 0;
     }
-  #else /* use times */
-    {  struct tms buf;
-       times( &buf );
-       (*add)( &buf, sizeof buf, 1 );
+
+
+
+    /* Set up the buffer */
+    gather_buffer_size = GATHER_BUFSIZE;
+    gather_buffer = malloc( gather_buffer_size );
+    if( !gather_buffer ) {
+       g10_log_error("out of core while allocating the gatherer buffer\n");
+       exit(2);
     }
+
+    /* 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
-  #ifdef HAVE_GETRUSAGE
-    {  struct rusage buf;
-       if( getrusage( RUSAGE_SELF, &buf ) )
-           BUG();
-       (*add)( &buf, sizeof buf, 1 );
-       memset( &buf, 0, sizeof buf );
+
+    fclose(stderr);            /* Arrghh!!  It's Stuart code!! */
+
+    for(;;) {
+       GATHER_MSG msg;
+       size_t nbytes;
+       const char *p;
+
+       msg.usefulness = slow_poll( dbgfp, dbgall, &nbytes );
+       p = gather_buffer;
+       while( nbytes ) {
+           msg.ndata = nbytes > sizeof(msg.data)? sizeof(msg.data) : nbytes;
+           memcpy( msg.data, p, msg.ndata );
+           nbytes -= msg.ndata;
+           p += msg.ndata;
+
+           while( write( pipefd, &msg, sizeof(msg) ) != sizeof(msg) ) {
+               if( errno == EINTR )
+                   continue;
+               if( errno == EAGAIN ) {
+                   struct timeval tv;
+                   tv.tv_sec = 0;
+                   tv.tv_usec = 50000;
+                   select(0, NULL, NULL, NULL, &tv);
+                   continue;
+               }
+               if( errno == EPIPE ) /* parent has exited, so give up */
+                  exit(0);
+
+               /* we can't do very much here because stderr is closed */
+               if( dbgfp )
+                   fprintf(dbgfp, "gatherer can't write to pipe: %s\n",
+                                   strerror(errno) );
+               /* we start a new poll to give the system some time */
+               nbytes = 0;
+               break;
+           }
+       }
     }
-  #endif
+    /* we are killed when the parent dies */
 }
 
 
+static int
+read_a_msg( int fd, GATHER_MSG *msg )
+{
+    char *buffer = (char*)msg;
+    size_t length = sizeof( *msg );
+    int n;
+
+    do {
+       do {
+           n = read(fd, buffer, length );
+       } while( n == -1 && errno == EINTR );
+       if( n == -1 )
+           return -1;
+       buffer += n;
+       length -= n;
+    } while( length );
+    return 0;
+}
+
 
 static int
-gather_random( byte *buffer, size_t *r_length, int level )
+gather_random( void (*add)(const void*, size_t, int), int requester,
+              size_t length, int level )
 {
-    GATHERER_INFO gathererInfo;
-    int status;
+    static pid_t gatherer_pid = 0;
+    static int pipedes[2];
+    GATHER_MSG msg;
     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;
+
+    if( !gatherer_pid ) {
+       /* make sure we are not setuid */
+       if( getuid() != geteuid() )
+           BUG();
+       /* time to start the gatherer process */
+       if( pipe( pipedes ) ) {
+           g10_log_error("pipe() failed: %s\n", strerror(errno));
+           return -1;
+       }
+       gatherer_pid = fork();
+       if( gatherer_pid == -1 ) {
+           g10_log_error("can't for gatherer process: %s\n", strerror(errno));
+           return -1;
+       }
+       if( !gatherer_pid ) {
+           start_gatherer( pipedes[1] );
+           /* oops, can't happen */
+           return -1;
+       }
+    }
+
+    /* now read from the gatherer */
+    while( length ) {
+       int goodness;
+       ulong subtract;
+
+       if( read_a_msg( pipedes[0], &msg ) ) {
+           g10_log_error("reading from gatherer pipe failed: %s\n",
+                                                           strerror(errno));
+           return -1;
+       }
+
+
+       if( level > 1 ) {
+           if( msg.usefulness > 30 )
+               goodness = 100;
+           else if ( msg.usefulness )
+               goodness = msg.usefulness * 100 / 30;
+           else
+               goodness = 0;
+       }
+       else if( level ) {
+           if( msg.usefulness > 15 )
+               goodness = 100;
+           else if ( msg.usefulness )
+               goodness = msg.usefulness * 100 / 15;
+           else
+               goodness = 0;
+       }
+       else
+           goodness = 100; /* goodness of level 0 is always 100 % */
+
+       n = msg.ndata;
+       if( n > length )
+           n = length;
+       (*add)( msg.data, n, requester );
+
+       /* this is the trick how e cope with the goodness */
+       subtract = (ulong)n * goodness / 100;
+       /* subtract at least 1 byte to avoid infinite loops */
+       length -= subtract ? subtract : 1;
+    }
+
+    return 0;
 }
 
 
@@ -698,7 +807,6 @@ static struct {
     void *func;
 } func_table[] = {
     { 40, 1, gather_random },
-    { 41, 1, fast_poll },
 };
 
 /****************