diff --git a/cipher/rndlinux.c b/cipher/rndlinux.c index 9d40f47b8..709a7ad54 100644 --- a/cipher/rndlinux.c +++ b/cipher/rndlinux.c @@ -1,159 +1,164 @@ /* rndlinux.c - raw random number for OSes with /dev/random * Copyright (C) 1998, 1999, 2000, 2001 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 3 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, see . */ #include #include #include #include #include #include #include #include #ifdef HAVE_GETTIMEOFDAY #include #endif #include #include #include #if 0 #include #include #include #endif #include "types.h" #include "util.h" #include "ttyio.h" #include "algorithms.h" #include "i18n.h" static int open_device( const char *name, int minor ); #if 0 #ifdef HAVE_DEV_RANDOM_IOCTL static ulong get_entropy_count( int fd ) { ulong count; if( ioctl( fd, RNDGETENTCNT, &count ) == -1 ) g10_log_fatal("ioctl(RNDGETENTCNT) failed: %s\n", strerror(errno) ); return count; } #endif #endif /* * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it * exists), ...) */ static int open_device( const char *name, int minor ) { int fd; struct stat sb; fd = open( name, O_RDONLY ); if( fd == -1 ) g10_log_fatal("can't open %s: %s\n", name, strerror(errno) ); if( fstat( fd, &sb ) ) g10_log_fatal("stat() off %s failed: %s\n", name, strerror(errno) ); /* Don't check device type for better portability */ /* if( (!S_ISCHR(sb.st_mode)) && (!S_ISFIFO(sb.st_mode)) ) g10_log_fatal("invalid random device!\n" ); */ return fd; } /* * Note: Using a level of 0 should never block and better add nothing * to the pool. This is easy to accomplish with /dev/urandom. */ int rndlinux_gather_random( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) { static int fd_urandom = -1; static int fd_random = -1; int fd; int n; int warn=0; byte buffer[768]; if( level >= 2 ) { if( fd_random == -1 ) fd_random = open_device( NAME_OF_DEV_RANDOM, 8 ); fd = fd_random; } else { /* This will also be used for level 0. By using /dev/urandom * we can be sure that it will never block. */ if( fd_urandom == -1 ) fd_urandom = open_device( NAME_OF_DEV_URANDOM, 9 ); fd = fd_urandom; } #if 0 #ifdef HAVE_DEV_RANDOM_IOCTL g10_log_info("entropy count of %d is %lu\n", fd, get_entropy_count(fd) ); #endif #endif while( length ) { +#ifdef FD_SETSIZE fd_set rfds; struct timeval tv; - int rc; + int rc; FD_ZERO(&rfds); - FD_SET(fd, &rfds); tv.tv_sec = 3; tv.tv_usec = 0; - if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) ) { - if( !warn ) + if (fd < FD_SETSIZE) + { + FD_SET(fd, &rfds); + if( !(rc=select(fd+1, &rfds, NULL, NULL, &tv)) ) { + if( !warn ) tty_printf( _("\n" "Not enough random bytes available. Please do some other work to give\n" "the OS a chance to collect more entropy! (Need %d more bytes)\n"), (int)length ); - warn = 1; - continue; - } - else if( rc == -1 ) { - tty_printf( - "select() error: %s\n", strerror(errno)); - continue; - } + warn = 1; + continue; + } + else if( rc == -1 ) { + tty_printf( + "select() error: %s\n", strerror(errno)); + continue; + } + } +#endif /*FD_SETSIZE*/ do { int nbytes = length < sizeof(buffer)? length : sizeof(buffer); n = read(fd, buffer, nbytes ); if( n >= 0 && n > nbytes ) { g10_log_error("bogus read from random device (n=%d)\n", n ); n = nbytes; } } while( n == -1 && errno == EINTR ); if( n == -1 ) g10_log_fatal("read error on random device: %s\n", strerror(errno)); (*add)( buffer, n, requester ); length -= n; } wipememory(buffer, sizeof(buffer) ); return 0; /* success */ } diff --git a/cipher/rndunix.c b/cipher/rndunix.c index 75cf22ed0..72905e63b 100644 --- a/cipher/rndunix.c +++ b/cipher/rndunix.c @@ -1,869 +1,871 @@ /**************************************************************************** * * * * * 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, 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 */ /* General includes */ #include #include #include #include #include /* OS-specific includes */ #ifdef __osf__ /* Somewhere in the morass of system-specific cruft which OSF/1 pulls in * via the following includes are various endianness defines, so we * undefine the cryptlib ones, which aren't really needed for this module * anyway */ #undef BIG_ENDIAN #undef LITTLE_ENDIAN #endif /* __osf__ */ #include #include #include #ifndef __QNX__ #include #include #endif /* __QNX__ */ #include /* SCO and SunOS need this before resource.h */ #ifndef __QNX__ #include #endif /* __QNX__ */ #if defined( _AIX ) || defined( __QNX__ ) #include #endif /* _AIX || __QNX__ */ #ifndef __QNX__ #include #include #include #endif /* __QNX__ */ #include #include /* Verschiedene komische Typen */ #if defined( __hpux ) && ( OS_VERSION == 9 ) #include #endif /* __hpux 9.x, after that it's in unistd.h */ #include /* #include */ #ifdef __QNX__ #include #include #endif /* __QNX__ */ #include #include "types.h" /* for byte and u32 typedefs */ #include "algorithms.h" #include "util.h" #ifndef EAGAIN #define EAGAIN EWOULDBLOCK #endif #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #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 * (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 int hasAlternative; /* Whether source has alt.location */ } dataSources[] = { { "/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, 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, 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, 0 }, /* IP ? */ { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.10.0", 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, 0 }, /* TCP ? */ { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.13.0", 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, 1 }, #endif /* __sgi || __hpux */ { "/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 }, { "/bin/ps", "-A", SC(0.3), NULL, 0, 0, 0, 0 }, /*QNX*/ { "/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, 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, 1 }, #ifdef __sgi { "/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, 0 }, #endif /* __hpux */ { "/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, 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, 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, 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, 0}, { "/usr/sbin/advfsstat", "-l 2 usr_domain", SC(0.5), NULL, 0, 0, 0, 0}, { "/usr/sbin/advfsstat", "-p usr_domain", 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 */ -#ifdef __QNXNTO__ +#ifdef __QNXNTO__ { "/bin/pidin", "-F%A%B%c%d%E%I%J%K%m%M%n%N%p%P%S%s%T", SC(0.3), NULL, 0, 0, 0, 0 }, -#endif +#endif #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, 0 }, { "/usr/local/bin/wget", "-O - http://lavarand.sgi.com/block.html", 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, 0 } }; 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 */ int ndata; /* valid bytes in data */ char data[500]; /* gathered data */ } GATHER_MSG; #ifndef HAVE_WAITPID pid_t waitpid(pid_t pid, int *statptr, int options) { #ifdef HAVE_WAIT4 return wait4(pid, statptr, options, NULL); #else /* If wait4 is also not available, try wait3 for SVR3 variants */ /* Less ideal because can't actually request a specific pid */ /* For that reason, first check to see if pid is for an */ /* existing process. */ int tmp_pid, dummystat;; if (kill(pid, 0) == -1) { errno = ECHILD; return -1; } if (statptr == NULL) statptr = &dummystat; while (((tmp_pid = wait3(statptr, options, 0)) != pid) && (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) ; return tmp_pid; #endif } #endif /* 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; int fd; /* We are the child. Make the read side of the pipe be stdout */ if (dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0) exit(127); /* Connect the other standard handles to the bit bucket. */ if ((fd = open ("/dev/null", O_RDWR)) != -1) { dup2 (fd, STDIN_FILENO); dup2 (fd, STDERR_FILENO); close (fd); } /* 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 (gatherer_uid == (uid_t)-1 && \ (passwd = getpwnam("nobody")) != NULL) gatherer_uid = passwd->pw_uid; setuid(gatherer_uid); /* 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]); #ifdef FD_CLOEXEC fcntl(pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC); #endif 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 */ static int slow_poll(FILE *dbgfp, int dbgall, size_t *nbytes ) { int moreSources; struct timeval tv; fd_set fds; #if defined( __hpux ) size_t maxFD = 0; #else int maxFD = 0; #endif /* OS-specific brokenness */ int bufPos, i, usefulness = 0; /* 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)) { if( dbgfp && dbgall ) fprintf(dbgfp, "%s not present%s\n", dataSources[i].path, dataSources[i].hasAlternative ? ", has alternatives" : ""); 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; #ifdef O_NONBLOCK /* Ohhh what a hack (used for Atari) */ fcntl(dataSources[i].pipeFD, F_SETFL, O_NONBLOCK); #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) { if( dbgfp && dbgall ) fprintf(dbgfp, "Skipping %s\n", dataSources[i + 1].path); i++; } } } /* Suck all the data we can get from each of the sources */ 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 * 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(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 < 0) total = (dataSources[i].length + 999) / -dataSources[i].usefulness; else total = dataSources[i].length / dataSources[i].usefulness; } 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); 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 = gather_buffer[currPos]; /* If it's a single byte, just copy it over */ if (ch != gather_buffer[currPos + 1]) { gather_buffer[bufPos++] = ch; currPos++; } else { int count = 0; /* It's a run of repeated bytes, replace them * with the byte count mod 256 */ while ((ch == gather_buffer[currPos]) && currPos < endPos) { count++; currPos++; } gather_buffer[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 = 0; FD_ZERO(&fds); for (i = 0; dataSources[i].path != NULL; i++) { if (dataSources[i].pipe != NULL) { + /* FIXME: We need to make sure that PIPEFD is less + than FD_SETSIZE. */ FD_SET(dataSources[i].pipeFD, &fds); moreSources = 1; } } } 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 start_gatherer( int pipefd ) { FILE *dbgfp = NULL; int dbgall; #ifdef ENABLE_SELINUX_HACKS /* We don't allow writing to the log file because this might be sued to corrupt a secured file. Given that this is used as a library by the ../g10/ code, we can't access the check function from ../g10/misc.c. */ dbgall = 0; #else { 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"); } #endif /* 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 fflush (stderr); /* Arrghh!! It's Stuart code!! */ /* (close all files but the ones we need) */ { int nmax, n1, i; #ifdef _SC_OPEN_MAX if( (nmax=sysconf( _SC_OPEN_MAX )) < 0 ) { #ifdef _POSIX_OPEN_MAX nmax = _POSIX_OPEN_MAX; #else nmax = 20; /* assume a reasonable value */ #endif } #else nmax = 20; /* assume a reasonable value */ #endif - { + { int fd; if ((fd = open ("/dev/null", O_RDWR)) != -1) { dup2 (fd, STDIN_FILENO); dup2 (fd, STDOUT_FILENO); dup2 (fd, STDERR_FILENO); close (fd); } } n1 = dbgfp? fileno (dbgfp) : -1; for(i=0; i < nmax; i++ ) { if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO && i != n1 && i != pipefd ) close(i); } errno = 0; } 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; } } } /* 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; } /**************** * Using a level of 0 should never block and better add nothing * to the pool. So this is just a dummy for this gatherer. */ int rndunix_gather_random( void (*add)(const void*, size_t, int), int requester, size_t length, int level ) { static pid_t gatherer_pid = 0; static int pipedes[2]; GATHER_MSG msg; size_t n; if( !level ) 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; }