1 /****************************************************************************
3 * BeOS Randomness-Gathering Code *
4 * Copyright Peter Gutmann, Paul Kendall, and Chris Wedgwood 1996-1998 *
5 * Copyright (C) 1998, 1999 Werner Koch
7 ****************************************************************************/
17 /* OS-specific includes */
20 /* Somewhere in the morass of system-specific cruft which OSF/1 pulls in
21 * via the following includes are various endianness defines, so we
22 * undefine the cryptlib ones, which aren't really needed for this module
32 #include <sys/errno.h>
35 #include <sys/time.h> /* SCO and SunOS need this before resource.h */
37 #include <sys/resource.h>
40 #include <sys/select.h>
44 #include <sys/signal.h>
47 #include <sys/types.h> /* Verschiedene komische Typen */
48 #if defined( __hpux ) && ( OS_VERSION == 9 )
50 #endif /* __hpux 9.x, after that it's in unistd.h */
52 /* #include <kitchensink.h> */
55 #include "types.h" /* for byte and u32 typedefs */
62 #define EAGAIN EWOULDBLOCK
65 #define GATHER_BUFSIZE 49152 /* Usually about 25K are filled */
67 /* The structure containing information on random-data sources. Each
68 * record contains the source and a relative estimate of its usefulness
69 * (weighting) which is used to scale the number of kB of output from the
70 * source (total = data_bytes / usefulness). Usually the weighting is in the
71 * range 1-3 (or 0 for especially useless sources), resulting in a usefulness
72 * rating of 1...3 for each kB of source output (or 0 for the useless
75 * If the source is constantly changing (certain types of network statistics
76 * have this characteristic) but the amount of output is small, the weighting
77 * is given as a negative value to indicate that the output should be treated
78 * as if a minimum of 1K of output had been obtained. If the source produces
79 * a lot of output then the scale factor is fractional, resulting in a
80 * usefulness rating of < 1 for each kB of source output.
82 * In order to provide enough randomness to satisfy the requirements for a
83 * slow poll, we need to accumulate at least 20 points of usefulness (a
84 * typical system should get about 30 points).
86 * Some potential options are missed out because of special considerations.
87 * pstat -i and pstat -f can produce amazing amounts of output (the record
88 * is 600K on an Oracle server) which floods the buffer and doesn't yield
89 * anything useful (apart from perhaps increasing the entropy of the vmstat
90 * output a bit), so we don't bother with this. pstat in general produces
91 * quite a bit of output, but it doesn't change much over time, so it gets
92 * very low weightings. netstat -s produces constantly-changing output but
93 * also produces quite a bit of it, so it only gets a weighting of 2 rather
94 * than 3. The same holds for netstat -in, which gets 1 rather than 2.
96 * Some binaries are stored in different locations on different systems so
97 * alternative paths are given for them. The code sorts out which one to
98 * run by itself, once it finds an exectable somewhere it moves on to the
99 * next source. The sources are arranged roughly in their order of
100 * usefulness, occasionally sources which provide a tiny amount of
101 * relatively useless data are placed ahead of ones which provide a large
102 * amount of possibly useful data because another 100 bytes can't hurt, and
103 * it means the buffer won't be swamped by one or two high-output sources.
104 * All the high-output sources are clustered towards the end of the list
105 * for this reason. Some binaries are checked for in a certain order, for
106 * example under Slowaris /usr/ucb/ps understands aux as an arg, but the
107 * others don't. Some systems have conditional defines enabling alternatives
108 * to commands which don't understand the usual options but will provide
109 * enough output (in the form of error messages) to look like they're the
110 * real thing, causing alternative options to be skipped (we can't check the
111 * return either because some commands return peculiar, non-zero status even
112 * when they're working correctly).
114 * In order to maximise use of the buffer, the code performs a form of run-
115 * length compression on its input where a repeated sequence of bytes is
116 * replaced by the occurrence count mod 256. Some commands output an awful
117 * lot of whitespace, this measure greatly increases the amount of data we
118 * can fit in the buffer.
120 * When we scale the weighting using the SC() macro, some preprocessors may
121 * give a division by zero warning for the most obvious expression
122 * 'weight ? 1024 / weight : 0' (and gcc 2.7.2.2 dies with a division by zero
123 * trap), so we define a value SC_0 which evaluates to zero when fed to
126 #define SC( weight ) ( 1024 / weight ) /* Scale factor */
127 #define SC_0 16384 /* SC( SC_0 ) evalutes to 0 */
130 const char *path; /* Path to check for existence of source */
131 const char *arg; /* Args for source */
132 const int usefulness; /* Usefulness of source */
133 FILE *pipe; /* Pipe to source as FILE * */
134 int pipeFD; /* Pipe to source as FD */
135 pid_t pid; /* pid of child for waitpid() */
136 int length; /* Quantity of output produced */
137 const int hasAlternative; /* Whether source has alt.location */
140 { "/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, 1 },
141 { "/usr/bin/vmstat", "-s", SC(-3), NULL, 0, 0, 0, 0},
142 { "/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, 1 },
143 { "/usr/bin/vmstat", "-c", SC(-3), NULL, 0, 0, 0, 0},
144 { "/usr/bin/pfstat", NULL, SC(-2), NULL, 0, 0, 0, 0},
145 { "/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, 1 },
146 { "/usr/bin/vmstat", "-i", SC(-2), NULL, 0, 0, 0, 0},
147 { "/usr/ucb/netstat", "-s", SC(2), NULL, 0, 0, 0, 1 },
148 { "/usr/bin/netstat", "-s", SC(2), NULL, 0, 0, 0, 1 },
149 { "/usr/sbin/netstat", "-s", SC(2), NULL, 0, 0, 0, 1},
150 { "/usr/etc/netstat", "-s", SC(2), NULL, 0, 0, 0, 0},
151 { "/usr/bin/nfsstat", NULL, SC(2), NULL, 0, 0, 0, 0},
152 { "/usr/ucb/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1 },
153 { "/usr/bin/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1 },
154 { "/usr/sbin/netstat", "-m", SC(-1), NULL, 0, 0, 0, 1 },
155 { "/usr/etc/netstat", "-m", SC(-1), NULL, 0, 0, 0, 0 },
156 { "/bin/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1 },
157 { "/usr/ucb/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1 },
158 { "/usr/bin/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1 },
159 { "/usr/sbin/netstat", "-in", SC(-1), NULL, 0, 0, 0, 1},
160 { "/usr/etc/netstat", "-in", SC(-1), NULL, 0, 0, 0, 0},
161 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.1.0",
162 SC(-1), NULL, 0, 0, 0, 0 }, /* UDP in */
163 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.4.0",
164 SC(-1), NULL, 0, 0, 0, 0 }, /* UDP out */
165 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.4.3.0",
166 SC(-1), NULL, 0, 0, 0, 0 }, /* IP ? */
167 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.10.0",
168 SC(-1), NULL, 0, 0, 0, 0 }, /* TCP ? */
169 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.11.0",
170 SC(-1), NULL, 0, 0, 0, 0 }, /* TCP ? */
171 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.13.0",
172 SC(-1), NULL, 0, 0, 0, 0 }, /* TCP ? */
173 { "/usr/bin/mpstat", NULL, SC(1), NULL, 0, 0, 0, 0 },
174 { "/usr/bin/w", NULL, SC(1), NULL, 0, 0, 0, 1 },
175 { "/usr/bsd/w", NULL, SC(1), NULL, 0, 0, 0, 0 },
176 { "/usr/bin/df", NULL, SC(1), NULL, 0, 0, 0, 1 },
177 { "/bin/df", NULL, SC(1), NULL, 0, 0, 0, 0 },
178 { "/usr/sbin/portstat", NULL, SC(1), NULL, 0, 0, 0, 0 },
179 { "/usr/bin/iostat", NULL, SC(SC_0), NULL, 0, 0, 0, 0 },
180 { "/usr/bin/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, 1 },
181 { "/usr/bsd/uptime", NULL, SC(SC_0), NULL, 0, 0, 0, 0 },
182 { "/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, 1 },
183 { "/usr/bin/vmstat", "-f", SC(SC_0), NULL, 0, 0, 0, 0 },
184 { "/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, 1 },
185 { "/usr/bin/vmstat", NULL, SC(SC_0), NULL, 0, 0, 0, 0 },
186 { "/usr/ucb/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1 },
187 { "/usr/bin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1 },
188 { "/usr/sbin/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 1 },
189 { "/usr/etc/netstat", "-n", SC(0.5), NULL, 0, 0, 0, 0 },
190 #if defined( __sgi ) || defined( __hpux )
191 { "/bin/ps", "-el", SC(0.3), NULL, 0, 0, 0, 1 },
192 #endif /* __sgi || __hpux */
193 { "/usr/ucb/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1 },
194 { "/usr/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 1 },
195 { "/bin/ps", "aux", SC(0.3), NULL, 0, 0, 0, 0 },
196 { "/usr/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 1 },
197 { "/bin/ipcs", "-a", SC(0.5), NULL, 0, 0, 0, 0 },
198 /* Unreliable source, depends on system usage */
199 { "/etc/pstat", "-p", SC(0.5), NULL, 0, 0, 0, 1 },
200 { "/bin/pstat", "-p", SC(0.5), NULL, 0, 0, 0, 0 },
201 { "/etc/pstat", "-S", SC(0.2), NULL, 0, 0, 0, 1 },
202 { "/bin/pstat", "-S", SC(0.2), NULL, 0, 0, 0, 0 },
203 { "/etc/pstat", "-v", SC(0.2), NULL, 0, 0, 0, 1 },
204 { "/bin/pstat", "-v", SC(0.2), NULL, 0, 0, 0, 0 },
205 { "/etc/pstat", "-x", SC(0.2), NULL, 0, 0, 0, 1 },
206 { "/bin/pstat", "-x", SC(0.2), NULL, 0, 0, 0, 0 },
207 { "/etc/pstat", "-t", SC(0.1), NULL, 0, 0, 0, 1 },
208 { "/bin/pstat", "-t", SC(0.1), NULL, 0, 0, 0, 0 },
209 /* pstat is your friend */
210 { "/usr/bin/last", "-n 50", SC(0.3), NULL, 0, 0, 0, 1 },
212 { "/usr/bsd/last", "-50", SC(0.3), NULL, 0, 0, 0, 0 },
215 { "/etc/last", "-50", SC(0.3), NULL, 0, 0, 0, 0 },
217 { "/usr/bsd/last", "-n 50", SC(0.3), NULL, 0, 0, 0, 0 },
218 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.1.0",
219 SC(0.1), NULL, 0, 0, 0, 0 }, /* ICMP ? */
220 { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.3.0",
221 SC(0.1), NULL, 0, 0, 0, 0 }, /* ICMP ? */
222 { "/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1 },
223 { "/usr/etc/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1 },
224 { "/usr/bin/arp", "-a", SC(0.1), NULL, 0, 0, 0, 1 },
225 { "/usr/sbin/arp", "-a", SC(0.1), NULL, 0, 0, 0, 0 },
226 { "/usr/sbin/ripquery", "-nw 1 127.0.0.1",
227 SC(0.1), NULL, 0, 0, 0, 0 },
228 { "/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 1 },
229 { "/usr/bin/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 1 },
230 { "/usr/ucb/lpstat", "-t", SC(0.1), NULL, 0, 0, 0, 0 },
231 { "/usr/bin/tcpdump", "-c 5 -efvvx", SC(1), NULL, 0, 0, 0, 0 },
232 /* This is very environment-dependant. If network traffic is low, it'll
233 * probably time out before delivering 5 packets, which is OK because
234 * it'll probably be fixed stuff like ARP anyway */
235 { "/usr/sbin/advfsstat", "-b usr_domain",
236 SC(SC_0), NULL, 0, 0, 0, 0},
237 { "/usr/sbin/advfsstat", "-l 2 usr_domain",
238 SC(0.5), NULL, 0, 0, 0, 0},
239 { "/usr/sbin/advfsstat", "-p usr_domain",
240 SC(SC_0), NULL, 0, 0, 0, 0},
241 /* This is a complex and screwball program. Some systems have things
242 * like rX_dmn, x = integer, for RAID systems, but the statistics are
245 /* The following aren't enabled since they're somewhat slow and not very
246 * unpredictable, however they give an indication of the sort of sources
247 * you can use (for example the finger might be more useful on a
248 * firewalled internal network) */
249 { "/usr/bin/finger", "@ml.media.mit.edu", SC(0.9), NULL, 0, 0, 0, 0 },
250 { "/usr/local/bin/wget", "-O - http://lavarand.sgi.com/block.html",
251 SC(0.9), NULL, 0, 0, 0, 0 },
252 { "/bin/cat", "/usr/spool/mqueue/syslog", SC(0.9), NULL, 0, 0, 0, 0 },
254 { NULL, NULL, 0, NULL, 0, 0, 0, 0 }
257 static byte *gather_buffer; /* buffer for gathering random noise */
258 static int gather_buffer_size; /* size of the memory buffer */
259 static uid_t gatherer_uid;
261 /* The message structure used to communicate with the parent */
263 int usefulness; /* usefulness of data */
264 int ndata; /* valid bytes in data */
265 char data[500]; /* gathered data */
268 /* Under SunOS popen() doesn't record the pid of the child process. When
269 * pclose() is called, instead of calling waitpid() for the correct child, it
270 * calls wait() repeatedly until the right child is reaped. The problem is
271 * that this reaps any other children that happen to have died at that
272 * moment, and when their pclose() comes along, the process hangs forever.
273 * The fix is to use a wrapper for popen()/pclose() which saves the pid in
274 * the dataSources structure (code adapted from GNU-libc's popen() call).
276 * Aut viam inveniam aut faciam */
279 my_popen(struct RI *entry)
285 /* Create the pipe */
286 if (pipe(pipedes) < 0)
289 /* Fork off the child ("vfork() is like an OS orgasm. All OS's want to
290 * do it, but most just end up faking it" - Chris Wedgwood). If your OS
291 * supports it, you should try to use vfork() here because it's somewhat
293 #if defined( sun ) || defined( __ultrix__ ) || defined( __osf__ ) || \
295 entry->pid = vfork();
298 #endif /* Unixen which have vfork() */
299 if (entry->pid == (pid_t) - 1) {
300 /* The fork failed */
306 if (entry->pid == (pid_t) 0) {
307 struct passwd *passwd;
309 /* We are the child. Make the read side of the pipe be stdout */
310 if (dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
313 /* Now that everything is set up, give up our permissions to make
314 * sure we don't read anything sensitive. If the getpwnam() fails,
315 * we default to -1, which is usually nobody */
316 if (gatherer_uid == (uid_t)-1 && \
317 (passwd = getpwnam("nobody")) != NULL)
318 gatherer_uid = passwd->pw_uid;
320 setuid(gatherer_uid);
322 /* Close the pipe descriptors */
323 close(pipedes[STDIN_FILENO]);
324 close(pipedes[STDOUT_FILENO]);
326 /* Try and exec the program */
327 execl(entry->path, entry->path, entry->arg, NULL);
329 /* Die if the exec failed */
333 /* We are the parent. Close the irrelevant side of the pipe and open
334 * the relevant side as a new stream. Mark our side of the pipe to
335 * close on exec, so new children won't see it */
336 close(pipedes[STDOUT_FILENO]);
338 fcntl(pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
340 stream = fdopen(pipedes[STDIN_FILENO], "r");
342 if (stream == NULL) {
343 int savedErrno = errno;
345 /* The stream couldn't be opened or the child structure couldn't be
346 * allocated. Kill the child and close the other side of the pipe */
347 kill(entry->pid, SIGKILL);
349 close(pipedes[STDOUT_FILENO]);
353 waitpid(entry->pid, NULL, 0);
364 my_pclose(struct RI *entry)
368 if (fclose(entry->pipe))
371 /* We ignore the return value from the process because some programs
372 * return funny values which would result in the input being discarded
373 * even if they executed successfully. This isn't a problem because the
374 * result data size threshold will filter out any programs which exit
375 * with a usage message without producing useful output */
376 if (waitpid(entry->pid, NULL, 0) != entry->pid)
385 /* Unix slow poll (without special support for Linux)
387 * If a few of the randomness sources create a large amount of output then
388 * the slowPoll() stops once the buffer has been filled (but before all the
389 * randomness sources have been sucked dry) so that the 'usefulness' factor
390 * remains below the threshold. For this reason the gatherer buffer has to
391 * be fairly sizeable on moderately loaded systems. This is something of a
392 * bug since the usefulness should be influenced by the amount of output as
393 * well as the source type */
397 slow_poll(FILE *dbgfp, int dbgall, size_t *nbytes )
402 #if defined( __hpux )
404 int pageSize = 4096; /* PHUX doesn't have getpagesize() */
405 #elif defined( _M_XENIX ) || defined( __aux )
406 int maxFD = 0, pageSize = 4096;/* Nor do others, but they get fd right */
408 int maxFD = 0, pageSize = getpagesize();
409 #endif /* OS-specific brokenness */
410 int bufPos, i, usefulness = 0;
413 /* Fire up each randomness source */
415 for (i = 0; dataSources[i].path != NULL; i++) {
416 /* Since popen() is a fairly heavy function, we check to see whether
417 * the executable exists before we try to run it */
418 if (access(dataSources[i].path, X_OK)) {
419 if( dbgfp && dbgall )
420 fprintf(dbgfp, "%s not present%s\n", dataSources[i].path,
421 dataSources[i].hasAlternative ?
422 ", has alternatives" : "");
423 dataSources[i].pipe = NULL;
426 dataSources[i].pipe = my_popen(&dataSources[i]);
428 if (dataSources[i].pipe != NULL) {
429 dataSources[i].pipeFD = fileno(dataSources[i].pipe);
430 if (dataSources[i].pipeFD > maxFD)
431 maxFD = dataSources[i].pipeFD;
432 #ifdef O_NONBLOCK /* Ohhh what a hack (used for Atari) */
433 fcntl(dataSources[i].pipeFD, F_SETFL, O_NONBLOCK);
435 #warning O_NONBLOCK is missing
437 FD_SET(dataSources[i].pipeFD, &fds);
438 dataSources[i].length = 0;
440 /* If there are alternatives for this command, don't try and
442 while (dataSources[i].hasAlternative) {
443 if( dbgfp && dbgall )
444 fprintf(dbgfp, "Skipping %s\n", dataSources[i + 1].path);
451 /* Suck all the data we can get from each of the sources */
454 while (moreSources && bufPos <= gather_buffer_size) {
455 /* Wait for data to become available from any of the sources, with a
456 * timeout of 10 seconds. This adds even more randomness since data
457 * becomes available in a nondeterministic fashion. Kudos to HP's QA
458 * department for managing to ship a select() which breaks its own
463 #if defined( __hpux ) && ( OS_VERSION == 9 )
464 if (select(maxFD + 1, (int *)&fds, NULL, NULL, &tv) == -1)
466 if (select(maxFD + 1, &fds, NULL, NULL, &tv) == -1)
470 /* One of the sources has data available, read it into the buffer */
471 for (i = 0; dataSources[i].path != NULL; i++) {
472 if( dataSources[i].pipe && FD_ISSET(dataSources[i].pipeFD, &fds)) {
475 if ((noBytes = fread(gather_buffer + bufPos, 1,
476 gather_buffer_size - bufPos,
477 dataSources[i].pipe)) == 0) {
478 if (my_pclose(&dataSources[i]) == 0) {
481 /* Try and estimate how much entropy we're getting
482 * from a data source */
483 if (dataSources[i].usefulness)
484 if (dataSources[i].usefulness < 0)
485 total = (dataSources[i].length + 999)
486 / -dataSources[i].usefulness;
488 total = dataSources[i].length
489 / dataSources[i].usefulness;
492 "%s %s contributed %d bytes, "
493 "usefulness = %d\n", dataSources[i].path,
494 (dataSources[i].arg != NULL) ?
495 dataSources[i].arg : "",
496 dataSources[i].length, total);
497 if( dataSources[i].length )
500 dataSources[i].pipe = NULL;
503 int currPos = bufPos;
504 int endPos = bufPos + noBytes;
506 /* Run-length compress the input byte sequence */
507 while (currPos < endPos) {
508 int ch = gather_buffer[currPos];
510 /* If it's a single byte, just copy it over */
511 if (ch != gather_buffer[currPos + 1]) {
512 gather_buffer[bufPos++] = ch;
518 /* It's a run of repeated bytes, replace them
519 * with the byte count mod 256 */
520 while ((ch == gather_buffer[currPos])
521 && currPos < endPos) {
525 gather_buffer[bufPos++] = count;
526 noBytes -= count - 1;
530 /* Remember the number of (compressed) bytes of input we
532 dataSources[i].length += noBytes;
537 /* Check if there is more input available on any of the sources */
540 for (i = 0; dataSources[i].path != NULL; i++) {
541 if (dataSources[i].pipe != NULL) {
542 FD_SET(dataSources[i].pipeFD, &fds);
549 fprintf(dbgfp, "Got %d bytes, usefulness = %d\n", bufPos, usefulness);
557 * Start the gatherer process which writes messages of
558 * type GATHERER_MSG to pipedes
561 start_gatherer( int pipefd )
567 const char *s = getenv("GNUPG_RNDUNIX_DBG");
569 dbgfp = (*s=='-' && !s[1])? stdout : fopen(s, "a");
571 g10_log_info("can't open debug file `%s': %s\n",
572 s, strerror(errno) );
574 fprintf(dbgfp,"\nSTART RNDUNIX DEBUG pid=%d\n", (int)getpid());
576 dbgall = !!getenv("GNUPG_RNDUNIX_DBGALL");
578 /* close all files but the ones we need */
579 { int nmax, n1, n2, i;
580 if( (nmax=sysconf( _SC_OPEN_MAX )) < 0 ) {
581 #ifdef _POSIX_OPEN_MAX
582 nmax = _POSIX_OPEN_MAX;
584 nmax = 20; /* assume a reasonable value */
587 n1 = fileno( stderr );
588 n2 = dbgfp? fileno( dbgfp ) : -1;
589 for(i=0; i < nmax; i++ ) {
590 if( i != n1 && i != n2 && i != pipefd )
598 /* Set up the buffer */
599 gather_buffer_size = GATHER_BUFSIZE;
600 gather_buffer = malloc( gather_buffer_size );
601 if( !gather_buffer ) {
602 g10_log_error("out of core while allocating the gatherer buffer\n");
606 /* Reset the SIGC(H)LD handler to the system default. This is necessary
607 * because if the program which cryptlib is a part of installs its own
608 * SIGC(H)LD handler, it will end up reaping the cryptlib children before
609 * cryptlib can. As a result, my_pclose() will call waitpid() on a
610 * process which has already been reaped by the installed handler and
611 * return an error, so the read data won't be added to the randomness
612 * pool. There are two types of SIGC(H)LD naming, the SysV SIGCLD and
613 * the BSD/Posix SIGCHLD, so we need to handle either possibility */
615 signal(SIGCLD, SIG_DFL);
617 signal(SIGCHLD, SIG_DFL);
620 fclose(stderr); /* Arrghh!! It's Stuart code!! */
627 msg.usefulness = slow_poll( dbgfp, dbgall, &nbytes );
630 msg.ndata = nbytes > sizeof(msg.data)? sizeof(msg.data) : nbytes;
631 memcpy( msg.data, p, msg.ndata );
635 while( write( pipefd, &msg, sizeof(msg) ) != sizeof(msg) ) {
638 if( errno == EAGAIN ) {
642 select(0, NULL, NULL, NULL, &tv);
645 if( errno == EPIPE ) /* parent has exited, so give up */
648 /* we can't do very much here because stderr is closed */
650 fprintf(dbgfp, "gatherer can't write to pipe: %s\n",
652 /* we start a new poll to give the system some time */
658 /* we are killed when the parent dies */
663 read_a_msg( int fd, GATHER_MSG *msg )
665 char *buffer = (char*)msg;
666 size_t length = sizeof( *msg );
671 n = read(fd, buffer, length );
672 } while( n == -1 && errno == EINTR );
683 gather_random( void (*add)(const void*, size_t, int), int requester,
684 size_t length, int level )
686 static pid_t gatherer_pid = 0;
687 static int pipedes[2];
691 if( !gatherer_pid ) {
692 /* make sure we are not setuid */
693 if( getuid() != geteuid() )
695 /* time to start the gatherer process */
696 if( pipe( pipedes ) ) {
697 g10_log_error("pipe() failed: %s\n", strerror(errno));
700 gatherer_pid = fork();
701 if( gatherer_pid == -1 ) {
702 g10_log_error("can't for gatherer process: %s\n", strerror(errno));
705 if( !gatherer_pid ) {
706 start_gatherer( pipedes[1] );
707 /* oops, can't happen */
712 /* now read from the gatherer */
717 if( read_a_msg( pipedes[0], &msg ) ) {
718 g10_log_error("reading from gatherer pipe failed: %s\n",
725 if( msg.usefulness > 30 )
727 else if ( msg.usefulness )
728 goodness = msg.usefulness * 100 / 30;
733 if( msg.usefulness > 15 )
735 else if ( msg.usefulness )
736 goodness = msg.usefulness * 100 / 15;
741 goodness = 100; /* goodness of level 0 is always 100 % */
746 (*add)( msg.data, n, requester );
748 /* this is the trick how e cope with the goodness */
749 subtract = (ulong)n * goodness / 100;
750 /* subtract at least 1 byte to avoid infinite loops */
751 length -= subtract ? subtract : 1;
762 const char * const gnupgext_version = "RNDUNIX ($Revision$)";
770 { 40, 1, gather_random },
774 * Enumerate the names of the functions together with informations about
775 * this function. Set sequence to an integer with a initial value of 0 and
777 * If what is 0 all kind of functions are returned.
778 * Return values: class := class of function:
779 * 10 = message digest algorithm info function
780 * 11 = integer with available md algorithms
781 * 20 = cipher algorithm info function
782 * 21 = integer with available cipher algorithms
783 * 30 = public key algorithm info function
784 * 31 = integer with available pubkey algorithms
785 * 40 = get read_random_source() function
786 * 41 = get fast_random_poll function
787 * version = interface version of the function/pointer
788 * (currently this is 1 for all functions)
795 gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
801 if ( i >= DIM(func_table) || i < 0 ) {
804 *class = func_table[i].class;
805 *vers = func_table[i].version;
806 ret = func_table[i].func;
808 } while ( what && what != *class );
816 rndunix_constructor(void)
818 register_internal_cipher_extension( gnupgext_version,
819 gnupgext_enum_func );