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