2002-06-29 13:31:13 +00:00
|
|
|
/****************************************************************************
|
|
|
|
* *
|
|
|
|
* *
|
|
|
|
* 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 */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* General includes */
|
|
|
|
|
|
|
|
#include <config.h>
|
2002-08-03 10:50:53 +00:00
|
|
|
|
|
|
|
#ifdef USE_RNDUNIX
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
/* 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__ */
|
|
|
|
#if defined( _AIX ) || defined( __QNX__ )
|
|
|
|
#include <sys/select.h>
|
|
|
|
#endif /* _AIX || __QNX__ */
|
|
|
|
#ifndef __QNX__
|
|
|
|
#include <sys/shm.h>
|
|
|
|
#include <signal.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> */
|
|
|
|
#ifdef __QNX__
|
|
|
|
#include <signal.h>
|
|
|
|
#include <process.h>
|
|
|
|
#endif /* __QNX__ */
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "types.h" /* for byte and u32 typedefs */
|
|
|
|
#ifndef IS_MODULE
|
2002-08-04 22:00:32 +00:00
|
|
|
#include "algorithms.h"
|
2002-06-29 13:31:13 +00:00
|
|
|
#endif
|
|
|
|
#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__
|
|
|
|
{ "/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
|
|
|
|
#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;
|
2002-10-12 14:51:32 +00:00
|
|
|
int fd;
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
/* We are the child. Make the read side of the pipe be stdout */
|
|
|
|
if (dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
|
|
|
|
exit(127);
|
2002-10-12 14:51:32 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2002-10-12 14:51:32 +00:00
|
|
|
/* Close the pipe descriptors. */
|
2002-06-29 13:31:13 +00:00
|
|
|
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) {
|
|
|
|
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;
|
|
|
|
|
|
|
|
{
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
2002-10-12 14:51:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-06-29 13:31:13 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2002-08-03 10:50:53 +00:00
|
|
|
int
|
|
|
|
rndunix_gather_random( void (*add)(const void*, size_t, int), int requester,
|
|
|
|
size_t length, int level )
|
2002-06-29 13:31:13 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-08-03 10:50:53 +00:00
|
|
|
#endif /*USE_RNDUNIX*/
|