blob: d8da7d3c02ce5638c858d9815675c88f03bed097 [file] [log] [blame]
Damien Miller62116dc2001-12-24 01:41:47 +11001/*
Damien Miller7b10ef42002-01-21 23:44:12 +11002 * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
Damien Miller62116dc2001-12-24 01:41:47 +11003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
Damien Miller3717cda2006-03-15 14:02:36 +110027#include <sys/types.h>
28#include <sys/resource.h>
29#include <sys/stat.h>
30#include <sys/wait.h>
Damien Miller8ec8c3e2006-07-10 20:35:38 +100031#include <sys/socket.h>
32
33#include <netinet/in.h>
Damien Miller3717cda2006-03-15 14:02:36 +110034
35#ifdef HAVE_SYS_UN_H
36# include <sys/un.h>
37#endif
38
39#include <signal.h>
40
Damien Miller62116dc2001-12-24 01:41:47 +110041#include <openssl/rand.h>
42#include <openssl/sha.h>
43#include <openssl/crypto.h>
44
45/* SunOS 4.4.4 needs this */
46#ifdef HAVE_FLOATINGPOINT_H
47# include <floatingpoint.h>
48#endif /* HAVE_FLOATINGPOINT_H */
49
50#include "misc.h"
51#include "xmalloc.h"
52#include "atomicio.h"
53#include "pathnames.h"
54#include "log.h"
55
Damien Miller7b10ef42002-01-21 23:44:12 +110056/* Number of bytes we write out */
57#define OUTPUT_SEED_SIZE 48
58
59/* Length of on-disk seedfiles */
60#define SEED_FILE_SIZE 1024
61
62/* Maximum number of command-line arguments to read from file */
63#define NUM_ARGS 10
64
65/* Minimum number of usable commands to be considered sufficient */
66#define MIN_ENTROPY_SOURCES 16
67
68/* Path to on-disk seed file (relative to user's home directory */
69#ifndef SSH_PRNG_SEED_FILE
70# define SSH_PRNG_SEED_FILE _PATH_SSH_USER_DIR"/prng_seed"
71#endif
72
73/* Path to PRNG commands list */
74#ifndef SSH_PRNG_COMMAND_FILE
Damien Miller05eda432002-02-10 18:32:28 +110075# define SSH_PRNG_COMMAND_FILE SSHDIR "/ssh_prng_cmds"
Damien Miller7b10ef42002-01-21 23:44:12 +110076#endif
77
Kevin Steves94435082001-12-25 04:32:58 +000078extern char *__progname;
Kevin Steves94435082001-12-25 04:32:58 +000079
Damien Miller62116dc2001-12-24 01:41:47 +110080#define WHITESPACE " \t\n"
81
82#ifndef RUSAGE_SELF
83# define RUSAGE_SELF 0
84#endif
85#ifndef RUSAGE_CHILDREN
86# define RUSAGE_CHILDREN 0
87#endif
88
Damien Millerc46cc542002-01-22 21:58:27 +110089#if !defined(PRNGD_SOCKET) && !defined(PRNGD_PORT)
Damien Miller7b10ef42002-01-21 23:44:12 +110090# define USE_SEED_FILES
Damien Miller62116dc2001-12-24 01:41:47 +110091#endif
92
Damien Miller7b10ef42002-01-21 23:44:12 +110093typedef struct {
94 /* Proportion of data that is entropy */
95 double rate;
96 /* Counter goes positive if this command times out */
97 unsigned int badness;
98 /* Increases by factor of two each timeout */
99 unsigned int sticky_badness;
100 /* Path to executable */
101 char *path;
102 /* argv to pass to executable */
103 char *args[NUM_ARGS]; /* XXX: arbitrary limit */
104 /* full command string (debug) */
105 char *cmdstring;
106} entropy_cmd_t;
107
108/* slow command timeouts (all in milliseconds) */
109/* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
110static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
111
112/* this is initialised from a file, by prng_read_commands() */
113static entropy_cmd_t *entropy_cmds = NULL;
114
115/* Prototypes */
116double stir_from_system(void);
117double stir_from_programs(void);
118double stir_gettimeofday(double entropy_estimate);
119double stir_clock(double entropy_estimate);
120double stir_rusage(int who, double entropy_estimate);
Kevin Steves4bdb5472002-07-28 20:42:23 +0000121double hash_command_output(entropy_cmd_t *src, unsigned char *hash);
Damien Millera8e06ce2003-11-21 23:48:55 +1100122int get_random_bytes_prngd(unsigned char *buf, int len,
Damien Miller7b10ef42002-01-21 23:44:12 +1100123 unsigned short tcp_port, char *socket_path);
124
125/*
126 * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon
127 * listening either on 'tcp_port', or via Unix domain socket at *
128 * 'socket_path'.
Damien Millera8e06ce2003-11-21 23:48:55 +1100129 * Either a non-zero tcp_port or a non-null socket_path must be
Damien Miller7b10ef42002-01-21 23:44:12 +1100130 * supplied.
131 * Returns 0 on success, -1 on error
132 */
Damien Miller62116dc2001-12-24 01:41:47 +1100133int
Damien Millera8e06ce2003-11-21 23:48:55 +1100134get_random_bytes_prngd(unsigned char *buf, int len,
Damien Miller7b10ef42002-01-21 23:44:12 +1100135 unsigned short tcp_port, char *socket_path)
Damien Miller62116dc2001-12-24 01:41:47 +1100136{
Damien Miller7b10ef42002-01-21 23:44:12 +1100137 int fd, addr_len, rval, errors;
Damien Miller52c8afe2005-06-19 10:19:43 +1000138 u_char msg[2];
Damien Miller7b10ef42002-01-21 23:44:12 +1100139 struct sockaddr_storage addr;
140 struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
141 struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr;
Damien Miller62116dc2001-12-24 01:41:47 +1100142 mysig_t old_sigpipe;
143
Damien Miller7b10ef42002-01-21 23:44:12 +1100144 /* Sanity checks */
145 if (socket_path == NULL && tcp_port == 0)
146 fatal("You must specify a port or a socket");
147 if (socket_path != NULL &&
148 strlen(socket_path) >= sizeof(addr_un->sun_path))
149 fatal("Random pool path is too long");
Damien Miller52c8afe2005-06-19 10:19:43 +1000150 if (len <= 0 || len > 255)
151 fatal("Too many bytes (%d) to read from PRNGD", len);
Damien Miller62116dc2001-12-24 01:41:47 +1100152
153 memset(&addr, '\0', sizeof(addr));
154
Damien Miller7b10ef42002-01-21 23:44:12 +1100155 if (tcp_port != 0) {
156 addr_in->sin_family = AF_INET;
157 addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
158 addr_in->sin_port = htons(tcp_port);
159 addr_len = sizeof(*addr_in);
160 } else {
161 addr_un->sun_family = AF_UNIX;
162 strlcpy(addr_un->sun_path, socket_path,
163 sizeof(addr_un->sun_path));
164 addr_len = offsetof(struct sockaddr_un, sun_path) +
165 strlen(socket_path) + 1;
166 }
Damien Miller62116dc2001-12-24 01:41:47 +1100167
168 old_sigpipe = mysignal(SIGPIPE, SIG_IGN);
169
Damien Miller7b10ef42002-01-21 23:44:12 +1100170 errors = 0;
171 rval = -1;
Damien Miller62116dc2001-12-24 01:41:47 +1100172reopen:
Damien Miller7b10ef42002-01-21 23:44:12 +1100173 fd = socket(addr.ss_family, SOCK_STREAM, 0);
Damien Miller62116dc2001-12-24 01:41:47 +1100174 if (fd == -1) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100175 error("Couldn't create socket: %s", strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100176 goto done;
177 }
Damien Miller62116dc2001-12-24 01:41:47 +1100178
179 if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100180 if (tcp_port != 0) {
181 error("Couldn't connect to PRNGD port %d: %s",
182 tcp_port, strerror(errno));
183 } else {
184 error("Couldn't connect to PRNGD socket \"%s\": %s",
185 addr_un->sun_path, strerror(errno));
186 }
Damien Miller62116dc2001-12-24 01:41:47 +1100187 goto done;
188 }
189
190 /* Send blocking read request to PRNGD */
191 msg[0] = 0x02;
192 msg[1] = len;
193
Darren Tucker8661b562003-07-06 15:20:46 +1000194 if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) {
Damien Miller62116dc2001-12-24 01:41:47 +1100195 if (errno == EPIPE && errors < 10) {
196 close(fd);
197 errors++;
198 goto reopen;
199 }
200 error("Couldn't write to PRNGD socket: %s",
201 strerror(errno));
202 goto done;
203 }
204
Damien Miller52c8afe2005-06-19 10:19:43 +1000205 if (atomicio(read, fd, buf, len) != (size_t)len) {
Damien Miller62116dc2001-12-24 01:41:47 +1100206 if (errno == EPIPE && errors < 10) {
207 close(fd);
208 errors++;
209 goto reopen;
210 }
211 error("Couldn't read from PRNGD socket: %s",
212 strerror(errno));
213 goto done;
214 }
215
Damien Miller7b10ef42002-01-21 23:44:12 +1100216 rval = 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100217done:
218 mysignal(SIGPIPE, old_sigpipe);
219 if (fd != -1)
220 close(fd);
Damien Miller7b10ef42002-01-21 23:44:12 +1100221 return rval;
Damien Miller62116dc2001-12-24 01:41:47 +1100222}
223
Darren Tucker8686ed72004-12-20 12:05:08 +1100224static int
225seed_from_prngd(unsigned char *buf, size_t bytes)
226{
227#ifdef PRNGD_PORT
228 debug("trying egd/prngd port %d", PRNGD_PORT);
229 if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0)
230 return 0;
231#endif
232#ifdef PRNGD_SOCKET
233 debug("trying egd/prngd socket %s", PRNGD_SOCKET);
234 if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0)
235 return 0;
236#endif
237 return -1;
238}
239
Damien Miller62116dc2001-12-24 01:41:47 +1100240double
241stir_gettimeofday(double entropy_estimate)
242{
243 struct timeval tv;
244
245 if (gettimeofday(&tv, NULL) == -1)
246 fatal("Couldn't gettimeofday: %s", strerror(errno));
247
248 RAND_add(&tv, sizeof(tv), entropy_estimate);
249
Damien Miller7b10ef42002-01-21 23:44:12 +1100250 return entropy_estimate;
Damien Miller62116dc2001-12-24 01:41:47 +1100251}
252
253double
254stir_clock(double entropy_estimate)
255{
256#ifdef HAVE_CLOCK
257 clock_t c;
258
259 c = clock();
260 RAND_add(&c, sizeof(c), entropy_estimate);
261
Damien Miller7b10ef42002-01-21 23:44:12 +1100262 return entropy_estimate;
Damien Miller62116dc2001-12-24 01:41:47 +1100263#else /* _HAVE_CLOCK */
Damien Miller7b10ef42002-01-21 23:44:12 +1100264 return 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100265#endif /* _HAVE_CLOCK */
266}
267
268double
269stir_rusage(int who, double entropy_estimate)
270{
271#ifdef HAVE_GETRUSAGE
272 struct rusage ru;
273
274 if (getrusage(who, &ru) == -1)
Damien Miller7b10ef42002-01-21 23:44:12 +1100275 return 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100276
277 RAND_add(&ru, sizeof(ru), entropy_estimate);
278
Damien Miller7b10ef42002-01-21 23:44:12 +1100279 return entropy_estimate;
Damien Miller62116dc2001-12-24 01:41:47 +1100280#else /* _HAVE_GETRUSAGE */
Damien Miller7b10ef42002-01-21 23:44:12 +1100281 return 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100282#endif /* _HAVE_GETRUSAGE */
283}
284
Damien Miller62116dc2001-12-24 01:41:47 +1100285static int
Damien Miller7b10ef42002-01-21 23:44:12 +1100286timeval_diff(struct timeval *t1, struct timeval *t2)
287{
Damien Miller62116dc2001-12-24 01:41:47 +1100288 int secdiff, usecdiff;
289
290 secdiff = t2->tv_sec - t1->tv_sec;
291 usecdiff = (secdiff*1000000) + (t2->tv_usec - t1->tv_usec);
292 return (int)(usecdiff / 1000);
293}
294
295double
Kevin Steves4bdb5472002-07-28 20:42:23 +0000296hash_command_output(entropy_cmd_t *src, unsigned char *hash)
Damien Miller62116dc2001-12-24 01:41:47 +1100297{
Damien Miller7b10ef42002-01-21 23:44:12 +1100298 char buf[8192];
Damien Miller62116dc2001-12-24 01:41:47 +1100299 fd_set rdset;
Damien Miller7b10ef42002-01-21 23:44:12 +1100300 int bytes_read, cmd_eof, error_abort, msec_elapsed, p[2];
301 int status, total_bytes_read;
302 static int devnull = -1;
Damien Miller62116dc2001-12-24 01:41:47 +1100303 pid_t pid;
Damien Miller62116dc2001-12-24 01:41:47 +1100304 SHA_CTX sha;
Damien Miller7b10ef42002-01-21 23:44:12 +1100305 struct timeval tv_start, tv_current;
Damien Miller62116dc2001-12-24 01:41:47 +1100306
307 debug3("Reading output from \'%s\'", src->cmdstring);
308
309 if (devnull == -1) {
310 devnull = open("/dev/null", O_RDWR);
311 if (devnull == -1)
Damien Millera8e06ce2003-11-21 23:48:55 +1100312 fatal("Couldn't open /dev/null: %s",
Damien Miller7b10ef42002-01-21 23:44:12 +1100313 strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100314 }
315
316 if (pipe(p) == -1)
317 fatal("Couldn't open pipe: %s", strerror(errno));
318
319 (void)gettimeofday(&tv_start, NULL); /* record start time */
320
321 switch (pid = fork()) {
322 case -1: /* Error */
323 close(p[0]);
324 close(p[1]);
325 fatal("Couldn't fork: %s", strerror(errno));
326 /* NOTREACHED */
327 case 0: /* Child */
328 dup2(devnull, STDIN_FILENO);
329 dup2(p[1], STDOUT_FILENO);
330 dup2(p[1], STDERR_FILENO);
331 close(p[0]);
332 close(p[1]);
333 close(devnull);
334
335 execv(src->path, (char**)(src->args));
Damien Miller7b10ef42002-01-21 23:44:12 +1100336
Damien Millera8e06ce2003-11-21 23:48:55 +1100337 debug("(child) Couldn't exec '%s': %s",
Damien Miller7b10ef42002-01-21 23:44:12 +1100338 src->cmdstring, strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100339 _exit(-1);
340 default: /* Parent */
341 break;
342 }
343
344 RAND_add(&pid, sizeof(&pid), 0.0);
345
346 close(p[1]);
347
348 /* Hash output from child */
349 SHA1_Init(&sha);
Damien Miller62116dc2001-12-24 01:41:47 +1100350
Damien Miller7b10ef42002-01-21 23:44:12 +1100351 cmd_eof = error_abort = msec_elapsed = total_bytes_read = 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100352 while (!error_abort && !cmd_eof) {
353 int ret;
354 struct timeval tv;
355 int msec_remaining;
356
357 (void) gettimeofday(&tv_current, 0);
Damien Miller7b10ef42002-01-21 23:44:12 +1100358 msec_elapsed = timeval_diff(&tv_start, &tv_current);
Damien Miller62116dc2001-12-24 01:41:47 +1100359 if (msec_elapsed >= entropy_timeout_current) {
360 error_abort=1;
361 continue;
362 }
363 msec_remaining = entropy_timeout_current - msec_elapsed;
364
365 FD_ZERO(&rdset);
366 FD_SET(p[0], &rdset);
Damien Miller7b10ef42002-01-21 23:44:12 +1100367 tv.tv_sec = msec_remaining / 1000;
Damien Miller62116dc2001-12-24 01:41:47 +1100368 tv.tv_usec = (msec_remaining % 1000) * 1000;
369
Damien Miller7b10ef42002-01-21 23:44:12 +1100370 ret = select(p[0] + 1, &rdset, NULL, NULL, &tv);
Damien Miller62116dc2001-12-24 01:41:47 +1100371
372 RAND_add(&tv, sizeof(tv), 0.0);
373
374 switch (ret) {
375 case 0:
376 /* timer expired */
377 error_abort = 1;
Damien Miller5a5da882002-10-21 10:13:35 +1000378 kill(pid, SIGINT);
Damien Miller62116dc2001-12-24 01:41:47 +1100379 break;
380 case 1:
381 /* command input */
382 do {
383 bytes_read = read(p[0], buf, sizeof(buf));
384 } while (bytes_read == -1 && errno == EINTR);
385 RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
386 if (bytes_read == -1) {
387 error_abort = 1;
388 break;
389 } else if (bytes_read) {
390 SHA1_Update(&sha, buf, bytes_read);
391 total_bytes_read += bytes_read;
392 } else {
393 cmd_eof = 1;
394 }
395 break;
396 case -1:
397 default:
398 /* error */
Damien Millera8e06ce2003-11-21 23:48:55 +1100399 debug("Command '%s': select() failed: %s",
Damien Miller7b10ef42002-01-21 23:44:12 +1100400 src->cmdstring, strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100401 error_abort = 1;
402 break;
403 }
404 }
405
406 SHA1_Final(hash, &sha);
407
408 close(p[0]);
409
410 debug3("Time elapsed: %d msec", msec_elapsed);
411
412 if (waitpid(pid, &status, 0) == -1) {
Damien Millerb6f72f52005-07-17 17:26:43 +1000413 error("Couldn't wait for child '%s' completion: %s",
414 src->cmdstring, strerror(errno));
Damien Miller7b10ef42002-01-21 23:44:12 +1100415 return 0.0;
Damien Miller62116dc2001-12-24 01:41:47 +1100416 }
417
418 RAND_add(&status, sizeof(&status), 0.0);
419
420 if (error_abort) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100421 /*
422 * Closing p[0] on timeout causes the entropy command to
Damien Millera8e06ce2003-11-21 23:48:55 +1100423 * SIGPIPE. Take whatever output we got, and mark this
424 * command as slow
Damien Miller7b10ef42002-01-21 23:44:12 +1100425 */
Damien Miller62116dc2001-12-24 01:41:47 +1100426 debug2("Command '%s' timed out", src->cmdstring);
427 src->sticky_badness *= 2;
428 src->badness = src->sticky_badness;
Damien Miller7b10ef42002-01-21 23:44:12 +1100429 return total_bytes_read;
Damien Miller62116dc2001-12-24 01:41:47 +1100430 }
431
432 if (WIFEXITED(status)) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100433 if (WEXITSTATUS(status) == 0) {
434 return total_bytes_read;
Damien Miller62116dc2001-12-24 01:41:47 +1100435 } else {
Damien Miller7b10ef42002-01-21 23:44:12 +1100436 debug2("Command '%s' exit status was %d",
437 src->cmdstring, WEXITSTATUS(status));
Damien Miller62116dc2001-12-24 01:41:47 +1100438 src->badness = src->sticky_badness = 128;
Damien Miller7b10ef42002-01-21 23:44:12 +1100439 return 0.0;
Damien Miller62116dc2001-12-24 01:41:47 +1100440 }
441 } else if (WIFSIGNALED(status)) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100442 debug2("Command '%s' returned on uncaught signal %d !",
443 src->cmdstring, status);
Damien Miller62116dc2001-12-24 01:41:47 +1100444 src->badness = src->sticky_badness = 128;
Damien Miller7b10ef42002-01-21 23:44:12 +1100445 return 0.0;
Damien Miller62116dc2001-12-24 01:41:47 +1100446 } else
Damien Miller7b10ef42002-01-21 23:44:12 +1100447 return 0.0;
448}
449
450double
451stir_from_system(void)
452{
453 double total_entropy_estimate;
454 long int i;
455
456 total_entropy_estimate = 0;
457
458 i = getpid();
459 RAND_add(&i, sizeof(i), 0.5);
460 total_entropy_estimate += 0.1;
461
462 i = getppid();
463 RAND_add(&i, sizeof(i), 0.5);
464 total_entropy_estimate += 0.1;
465
466 i = getuid();
467 RAND_add(&i, sizeof(i), 0.0);
468 i = getgid();
469 RAND_add(&i, sizeof(i), 0.0);
470
471 total_entropy_estimate += stir_gettimeofday(1.0);
472 total_entropy_estimate += stir_clock(0.5);
473 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
474
475 return total_entropy_estimate;
476}
477
478double
479stir_from_programs(void)
480{
481 int c;
482 double entropy, total_entropy;
Kevin Steves4bdb5472002-07-28 20:42:23 +0000483 unsigned char hash[SHA_DIGEST_LENGTH];
Damien Miller7b10ef42002-01-21 23:44:12 +1100484
485 total_entropy = 0;
486 for(c = 0; entropy_cmds[c].path != NULL; c++) {
487 if (!entropy_cmds[c].badness) {
488 /* Hash output from command */
489 entropy = hash_command_output(&entropy_cmds[c],
490 hash);
491
492 /* Scale back estimate by command's rate */
493 entropy *= entropy_cmds[c].rate;
494
495 /* Upper bound of entropy is SHA_DIGEST_LENGTH */
496 if (entropy > SHA_DIGEST_LENGTH)
497 entropy = SHA_DIGEST_LENGTH;
498
499 /* Stir it in */
500 RAND_add(hash, sizeof(hash), entropy);
501
Damien Millera8e06ce2003-11-21 23:48:55 +1100502 debug3("Got %0.2f bytes of entropy from '%s'",
Damien Miller7b10ef42002-01-21 23:44:12 +1100503 entropy, entropy_cmds[c].cmdstring);
504
505 total_entropy += entropy;
506
507 /* Execution time should be a bit unpredictable */
508 total_entropy += stir_gettimeofday(0.05);
509 total_entropy += stir_clock(0.05);
510 total_entropy += stir_rusage(RUSAGE_SELF, 0.1);
511 total_entropy += stir_rusage(RUSAGE_CHILDREN, 0.1);
512 } else {
513 debug2("Command '%s' disabled (badness %d)",
Damien Millera8e06ce2003-11-21 23:48:55 +1100514 entropy_cmds[c].cmdstring,
Damien Miller7b10ef42002-01-21 23:44:12 +1100515 entropy_cmds[c].badness);
516
517 if (entropy_cmds[c].badness > 0)
518 entropy_cmds[c].badness--;
519 }
520 }
521
522 return total_entropy;
Damien Miller62116dc2001-12-24 01:41:47 +1100523}
524
525/*
526 * prng seedfile functions
527 */
528int
Damien Miller7b10ef42002-01-21 23:44:12 +1100529prng_check_seedfile(char *filename)
530{
Damien Miller62116dc2001-12-24 01:41:47 +1100531 struct stat st;
532
Damien Miller7b10ef42002-01-21 23:44:12 +1100533 /*
Damien Millera8e06ce2003-11-21 23:48:55 +1100534 * XXX raceable: eg replace seed between this stat and subsequent
535 * open. Not such a problem because we don't really trust the
Damien Miller7b10ef42002-01-21 23:44:12 +1100536 * seed file anyway.
537 * XXX: use secure path checking as elsewhere in OpenSSH
538 */
Damien Miller62116dc2001-12-24 01:41:47 +1100539 if (lstat(filename, &st) == -1) {
540 /* Give up on hard errors */
541 if (errno != ENOENT)
Damien Miller7b10ef42002-01-21 23:44:12 +1100542 debug("WARNING: Couldn't stat random seed file "
543 "\"%.100s\": %s", filename, strerror(errno));
544 return 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100545 }
546
547 /* regular file? */
548 if (!S_ISREG(st.st_mode))
Damien Miller7b10ef42002-01-21 23:44:12 +1100549 fatal("PRNG seedfile %.100s is not a regular file",
550 filename);
Damien Miller62116dc2001-12-24 01:41:47 +1100551
552 /* mode 0600, owned by root or the current user? */
553 if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100554 debug("WARNING: PRNG seedfile %.100s must be mode 0600, "
Damien Millerc46b6bc2003-05-16 15:51:44 +1000555 "owned by uid %li", filename, (long int)getuid());
Damien Miller7b10ef42002-01-21 23:44:12 +1100556 return 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100557 }
558
Damien Miller7b10ef42002-01-21 23:44:12 +1100559 return 1;
Damien Miller62116dc2001-12-24 01:41:47 +1100560}
561
562void
Damien Miller7b10ef42002-01-21 23:44:12 +1100563prng_write_seedfile(void)
564{
Damien Millered462d92005-02-16 13:02:45 +1100565 int fd, save_errno;
Kevin Steves4bdb5472002-07-28 20:42:23 +0000566 unsigned char seed[SEED_FILE_SIZE];
Damien Millered462d92005-02-16 13:02:45 +1100567 char filename[MAXPATHLEN], tmpseed[MAXPATHLEN];
Damien Miller62116dc2001-12-24 01:41:47 +1100568 struct passwd *pw;
Damien Millered462d92005-02-16 13:02:45 +1100569 mode_t old_umask;
Damien Miller62116dc2001-12-24 01:41:47 +1100570
571 pw = getpwuid(getuid());
572 if (pw == NULL)
Damien Miller7b10ef42002-01-21 23:44:12 +1100573 fatal("Couldn't get password entry for current user "
Damien Millerc46b6bc2003-05-16 15:51:44 +1000574 "(%li): %s", (long int)getuid(), strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100575
576 /* Try to ensure that the parent directory is there */
577 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
Damien Miller7b10ef42002-01-21 23:44:12 +1100578 _PATH_SSH_USER_DIR);
Darren Tuckerdaf6ff42006-07-05 21:35:48 +1000579 if (mkdir(filename, 0700) < 0 && errno != EEXIST)
580 fatal("mkdir %.200s: %s", filename, strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100581
582 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
Damien Miller7b10ef42002-01-21 23:44:12 +1100583 SSH_PRNG_SEED_FILE);
Damien Miller62116dc2001-12-24 01:41:47 +1100584
Damien Millered462d92005-02-16 13:02:45 +1100585 strlcpy(tmpseed, filename, sizeof(tmpseed));
586 if (strlcat(tmpseed, ".XXXXXXXXXX", sizeof(tmpseed)) >=
587 sizeof(tmpseed))
588 fatal("PRNG seed filename too long");
Damien Miller62116dc2001-12-24 01:41:47 +1100589
Damien Millercafbcc72003-03-17 16:13:53 +1100590 if (RAND_bytes(seed, sizeof(seed)) <= 0)
Ben Lindstromda4d9cf2003-09-22 15:36:15 +0000591 fatal("PRNG seed extraction failed");
Damien Miller62116dc2001-12-24 01:41:47 +1100592
593 /* Don't care if the seed doesn't exist */
594 prng_check_seedfile(filename);
595
Damien Millered462d92005-02-16 13:02:45 +1100596 old_umask = umask(0177);
597
598 if ((fd = mkstemp(tmpseed)) == -1) {
599 debug("WARNING: couldn't make temporary PRNG seedfile %.100s "
600 "(%.100s)", tmpseed, strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100601 } else {
Damien Millered462d92005-02-16 13:02:45 +1100602 debug("writing PRNG seed to file %.100s", tmpseed);
603 if (atomicio(vwrite, fd, &seed, sizeof(seed)) < sizeof(seed)) {
604 save_errno = errno;
605 close(fd);
606 unlink(tmpseed);
Damien Miller7b10ef42002-01-21 23:44:12 +1100607 fatal("problem writing PRNG seedfile %.100s "
Damien Millered462d92005-02-16 13:02:45 +1100608 "(%.100s)", filename, strerror(save_errno));
609 }
Damien Miller62116dc2001-12-24 01:41:47 +1100610 close(fd);
Damien Millered462d92005-02-16 13:02:45 +1100611 debug("moving temporary PRNG seed to file %.100s", filename);
612 if (rename(tmpseed, filename) == -1) {
613 save_errno = errno;
614 unlink(tmpseed);
615 fatal("problem renaming PRNG seedfile from %.100s "
Damien Miller94cf4c82005-07-17 17:04:47 +1000616 "to %.100s (%.100s)", tmpseed, filename,
Damien Millered462d92005-02-16 13:02:45 +1100617 strerror(save_errno));
618 }
Damien Miller62116dc2001-12-24 01:41:47 +1100619 }
Damien Millered462d92005-02-16 13:02:45 +1100620 umask(old_umask);
Damien Miller62116dc2001-12-24 01:41:47 +1100621}
622
623void
Damien Miller7b10ef42002-01-21 23:44:12 +1100624prng_read_seedfile(void)
625{
Damien Miller62116dc2001-12-24 01:41:47 +1100626 int fd;
Damien Miller7b10ef42002-01-21 23:44:12 +1100627 char seed[SEED_FILE_SIZE], filename[MAXPATHLEN];
Damien Miller62116dc2001-12-24 01:41:47 +1100628 struct passwd *pw;
629
630 pw = getpwuid(getuid());
631 if (pw == NULL)
Damien Miller7b10ef42002-01-21 23:44:12 +1100632 fatal("Couldn't get password entry for current user "
Damien Millerc46b6bc2003-05-16 15:51:44 +1000633 "(%li): %s", (long int)getuid(), strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100634
635 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
636 SSH_PRNG_SEED_FILE);
637
638 debug("loading PRNG seed from file %.100s", filename);
639
640 if (!prng_check_seedfile(filename)) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100641 verbose("Random seed file not found or invalid, ignoring.");
Damien Miller62116dc2001-12-24 01:41:47 +1100642 return;
643 }
644
645 /* open the file and read in the seed */
646 fd = open(filename, O_RDONLY);
647 if (fd == -1)
Damien Miller7b10ef42002-01-21 23:44:12 +1100648 fatal("could not open PRNG seedfile %.100s (%.100s)",
649 filename, strerror(errno));
Damien Miller62116dc2001-12-24 01:41:47 +1100650
Damien Miller7b10ef42002-01-21 23:44:12 +1100651 if (atomicio(read, fd, &seed, sizeof(seed)) < sizeof(seed)) {
652 verbose("invalid or short read from PRNG seedfile "
653 "%.100s - ignoring", filename);
Damien Miller62116dc2001-12-24 01:41:47 +1100654 memset(seed, '\0', sizeof(seed));
655 }
656 close(fd);
657
658 /* stir in the seed, with estimated entropy zero */
659 RAND_add(&seed, sizeof(seed), 0.0);
660}
661
662
663/*
664 * entropy command initialisation functions
665 */
666int
667prng_read_commands(char *cmdfilename)
668{
Damien Miller7b10ef42002-01-21 23:44:12 +1100669 char cmd[SEED_FILE_SIZE], *cp, line[1024], path[SEED_FILE_SIZE];
Damien Miller62116dc2001-12-24 01:41:47 +1100670 double est;
Damien Miller7b10ef42002-01-21 23:44:12 +1100671 entropy_cmd_t *entcmd;
672 FILE *f;
673 int cur_cmd, linenum, num_cmds, arg;
Damien Miller62116dc2001-12-24 01:41:47 +1100674
Damien Miller7b10ef42002-01-21 23:44:12 +1100675 if ((f = fopen(cmdfilename, "r")) == NULL) {
Damien Miller62116dc2001-12-24 01:41:47 +1100676 fatal("couldn't read entropy commands file %.100s: %.100s",
677 cmdfilename, strerror(errno));
678 }
679
Damien Miller7b10ef42002-01-21 23:44:12 +1100680 num_cmds = 64;
Darren Tuckerd8093e42006-05-04 16:24:34 +1000681 entcmd = xcalloc(num_cmds, sizeof(entropy_cmd_t));
Damien Miller62116dc2001-12-24 01:41:47 +1100682
683 /* Read in file */
Damien Miller7b10ef42002-01-21 23:44:12 +1100684 cur_cmd = linenum = 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100685 while (fgets(line, sizeof(line), f)) {
Damien Miller62116dc2001-12-24 01:41:47 +1100686 linenum++;
687
Damien Miller7b10ef42002-01-21 23:44:12 +1100688 /* Skip leading whitespace, blank lines and comments */
Damien Miller62116dc2001-12-24 01:41:47 +1100689 cp = line + strspn(line, WHITESPACE);
690 if ((*cp == 0) || (*cp == '#'))
691 continue; /* done with this line */
692
Damien Miller7b10ef42002-01-21 23:44:12 +1100693 /*
Damien Millera8e06ce2003-11-21 23:48:55 +1100694 * The first non-whitespace char should be a double quote
Damien Miller7b10ef42002-01-21 23:44:12 +1100695 * delimiting the commandline
696 */
Damien Miller62116dc2001-12-24 01:41:47 +1100697 if (*cp != '"') {
Damien Miller7b10ef42002-01-21 23:44:12 +1100698 error("bad entropy command, %.100s line %d",
699 cmdfilename, linenum);
Damien Miller62116dc2001-12-24 01:41:47 +1100700 continue;
701 }
702
Damien Miller7b10ef42002-01-21 23:44:12 +1100703 /*
704 * First token, command args (incl. argv[0]) in double
705 * quotes
706 */
Damien Miller62116dc2001-12-24 01:41:47 +1100707 cp = strtok(cp, "\"");
708 if (cp == NULL) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100709 error("missing or bad command string, %.100s "
710 "line %d -- ignored", cmdfilename, linenum);
Damien Miller62116dc2001-12-24 01:41:47 +1100711 continue;
712 }
713 strlcpy(cmd, cp, sizeof(cmd));
714
Damien Miller7b10ef42002-01-21 23:44:12 +1100715 /* Second token, full command path */
Damien Miller62116dc2001-12-24 01:41:47 +1100716 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100717 error("missing command path, %.100s "
718 "line %d -- ignored", cmdfilename, linenum);
Damien Miller62116dc2001-12-24 01:41:47 +1100719 continue;
720 }
721
Damien Miller7b10ef42002-01-21 23:44:12 +1100722 /* Did configure mark this as dead? */
Damien Miller62116dc2001-12-24 01:41:47 +1100723 if (strncmp("undef", cp, 5) == 0)
724 continue;
725
726 strlcpy(path, cp, sizeof(path));
727
Damien Miller7b10ef42002-01-21 23:44:12 +1100728 /* Third token, entropy rate estimate for this command */
Damien Miller62116dc2001-12-24 01:41:47 +1100729 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100730 error("missing entropy estimate, %.100s "
731 "line %d -- ignored", cmdfilename, linenum);
Damien Miller62116dc2001-12-24 01:41:47 +1100732 continue;
733 }
Damien Miller7b10ef42002-01-21 23:44:12 +1100734 est = strtod(cp, NULL);
Damien Miller62116dc2001-12-24 01:41:47 +1100735
736 /* end of line */
737 if ((cp = strtok(NULL, WHITESPACE)) != NULL) {
Damien Miller7b10ef42002-01-21 23:44:12 +1100738 error("garbage at end of line %d in %.100s "
739 "-- ignored", linenum, cmdfilename);
Damien Miller62116dc2001-12-24 01:41:47 +1100740 continue;
741 }
742
743 /* save the command for debug messages */
744 entcmd[cur_cmd].cmdstring = xstrdup(cmd);
745
746 /* split the command args */
747 cp = strtok(cmd, WHITESPACE);
748 arg = 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100749 do {
Damien Miller7b10ef42002-01-21 23:44:12 +1100750 entcmd[cur_cmd].args[arg] = xstrdup(cp);
Damien Miller62116dc2001-12-24 01:41:47 +1100751 arg++;
Damien Miller7b10ef42002-01-21 23:44:12 +1100752 } while(arg < NUM_ARGS && (cp = strtok(NULL, WHITESPACE)));
Damien Miller62116dc2001-12-24 01:41:47 +1100753
754 if (strtok(NULL, WHITESPACE))
Damien Miller7b10ef42002-01-21 23:44:12 +1100755 error("ignored extra commands (max %d), %.100s "
756 "line %d", NUM_ARGS, cmdfilename, linenum);
Damien Miller62116dc2001-12-24 01:41:47 +1100757
758 /* Copy the command path and rate estimate */
759 entcmd[cur_cmd].path = xstrdup(path);
760 entcmd[cur_cmd].rate = est;
761
762 /* Initialise other values */
763 entcmd[cur_cmd].sticky_badness = 1;
764
765 cur_cmd++;
766
Damien Miller7b10ef42002-01-21 23:44:12 +1100767 /*
768 * If we've filled the array, reallocate it twice the size
Damien Millera8e06ce2003-11-21 23:48:55 +1100769 * Do this now because even if this we're on the last
Damien Miller7b10ef42002-01-21 23:44:12 +1100770 * command we need another slot to mark the last entry
771 */
Damien Miller62116dc2001-12-24 01:41:47 +1100772 if (cur_cmd == num_cmds) {
773 num_cmds *= 2;
Damien Miller36812092006-03-26 14:22:47 +1100774 entcmd = xrealloc(entcmd, num_cmds,
Damien Miller7b10ef42002-01-21 23:44:12 +1100775 sizeof(entropy_cmd_t));
Damien Miller62116dc2001-12-24 01:41:47 +1100776 }
777 }
778
779 /* zero the last entry */
Damien Miller7b10ef42002-01-21 23:44:12 +1100780 memset(&entcmd[cur_cmd], '\0', sizeof(entropy_cmd_t));
Damien Miller62116dc2001-12-24 01:41:47 +1100781
782 /* trim to size */
Damien Miller36812092006-03-26 14:22:47 +1100783 entropy_cmds = xrealloc(entcmd, (cur_cmd + 1),
Damien Miller7b10ef42002-01-21 23:44:12 +1100784 sizeof(entropy_cmd_t));
Damien Miller62116dc2001-12-24 01:41:47 +1100785
Damien Miller7b10ef42002-01-21 23:44:12 +1100786 debug("Loaded %d entropy commands from %.100s", cur_cmd,
787 cmdfilename);
Damien Miller62116dc2001-12-24 01:41:47 +1100788
Darren Tuckerf58b29d2006-05-17 22:24:56 +1000789 fclose(f);
Damien Miller7b10ef42002-01-21 23:44:12 +1100790 return cur_cmd < MIN_ENTROPY_SOURCES ? -1 : 0;
Damien Miller62116dc2001-12-24 01:41:47 +1100791}
792
Damien Miller32e48182002-04-14 19:27:12 +1000793void
794usage(void)
795{
796 fprintf(stderr, "Usage: %s [options]\n", __progname);
797 fprintf(stderr, " -v Verbose; display verbose debugging messages.\n");
798 fprintf(stderr, " Multiple -v increases verbosity.\n");
Damien Miller7daf0442004-08-23 21:52:08 +1000799 fprintf(stderr, " -x Force output in hexadecimal (for debugging)\n");
Damien Miller32e48182002-04-14 19:27:12 +1000800 fprintf(stderr, " -X Force output in binary\n");
801 fprintf(stderr, " -b bytes Number of bytes to output (default %d)\n",
802 OUTPUT_SEED_SIZE);
803}
804
Damien Millera8e06ce2003-11-21 23:48:55 +1100805int
Damien Miller62116dc2001-12-24 01:41:47 +1100806main(int argc, char **argv)
807{
Damien Miller32e48182002-04-14 19:27:12 +1000808 unsigned char *buf;
809 int ret, ch, debug_level, output_hex, bytes;
810 extern char *optarg;
811 LogLevel ll;
Damien Miller62116dc2001-12-24 01:41:47 +1100812
Damien Miller59d3d5b2003-08-22 09:34:41 +1000813 __progname = ssh_get_progname(argv[0]);
Damien Miller62116dc2001-12-24 01:41:47 +1100814 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
815
Damien Miller32e48182002-04-14 19:27:12 +1000816 ll = SYSLOG_LEVEL_INFO;
817 debug_level = output_hex = 0;
818 bytes = OUTPUT_SEED_SIZE;
819
820 /* Don't write binary data to a tty, unless we are forced to */
821 if (isatty(STDOUT_FILENO))
822 output_hex = 1;
Damien Miller787b2ec2003-11-21 23:56:47 +1100823
Damien Miller32e48182002-04-14 19:27:12 +1000824 while ((ch = getopt(argc, argv, "vxXhb:")) != -1) {
825 switch (ch) {
826 case 'v':
827 if (debug_level < 3)
828 ll = SYSLOG_LEVEL_DEBUG1 + debug_level++;
829 break;
830 case 'x':
831 output_hex = 1;
832 break;
833 case 'X':
834 output_hex = 0;
835 break;
836 case 'b':
837 if ((bytes = atoi(optarg)) <= 0)
838 fatal("Invalid number of output bytes");
839 break;
840 case 'h':
841 usage();
842 exit(0);
843 default:
844 error("Invalid commandline option");
845 usage();
846 }
847 }
848
849 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
Damien Miller787b2ec2003-11-21 23:56:47 +1100850
Damien Miller7b10ef42002-01-21 23:44:12 +1100851#ifdef USE_SEED_FILES
852 prng_read_seedfile();
853#endif
854
Damien Miller32e48182002-04-14 19:27:12 +1000855 buf = xmalloc(bytes);
856
Damien Miller7b10ef42002-01-21 23:44:12 +1100857 /*
858 * Seed the RNG from wherever we can
859 */
Damien Miller787b2ec2003-11-21 23:56:47 +1100860
Damien Miller7b10ef42002-01-21 23:44:12 +1100861 /* Take whatever is on the stack, but don't credit it */
Damien Miller32e48182002-04-14 19:27:12 +1000862 RAND_add(buf, bytes, 0);
Damien Miller7b10ef42002-01-21 23:44:12 +1100863
Damien Millera8e06ce2003-11-21 23:48:55 +1100864 debug("Seeded RNG with %i bytes from system calls",
Damien Miller7b10ef42002-01-21 23:44:12 +1100865 (int)stir_from_system());
866
Darren Tucker8686ed72004-12-20 12:05:08 +1100867 /* try prngd, fall back to commands if prngd fails or not configured */
868 if (seed_from_prngd(buf, bytes) == 0) {
869 RAND_add(buf, bytes, bytes);
870 } else {
871 /* Read in collection commands */
872 if (prng_read_commands(SSH_PRNG_COMMAND_FILE) == -1)
873 fatal("PRNG initialisation failed -- exiting.");
874 debug("Seeded RNG with %i bytes from programs",
875 (int)stir_from_programs());
876 }
Damien Miller7b10ef42002-01-21 23:44:12 +1100877
878#ifdef USE_SEED_FILES
879 prng_write_seedfile();
880#endif
881
882 /*
883 * Write the seed to stdout
884 */
Damien Miller62116dc2001-12-24 01:41:47 +1100885
886 if (!RAND_status())
887 fatal("Not enough entropy in RNG");
888
Damien Millercafbcc72003-03-17 16:13:53 +1100889 if (RAND_bytes(buf, bytes) <= 0)
890 fatal("Couldn't extract entropy from PRNG");
Damien Miller62116dc2001-12-24 01:41:47 +1100891
Damien Miller32e48182002-04-14 19:27:12 +1000892 if (output_hex) {
893 for(ret = 0; ret < bytes; ret++)
894 printf("%02x", (unsigned char)(buf[ret]));
895 printf("\n");
896 } else
Darren Tucker8661b562003-07-06 15:20:46 +1000897 ret = atomicio(vwrite, STDOUT_FILENO, buf, bytes);
Damien Miller787b2ec2003-11-21 23:56:47 +1100898
Damien Miller32e48182002-04-14 19:27:12 +1000899 memset(buf, '\0', bytes);
900 xfree(buf);
Damien Miller787b2ec2003-11-21 23:56:47 +1100901
Damien Miller32e48182002-04-14 19:27:12 +1000902 return ret == bytes ? 0 : 1;
Damien Miller62116dc2001-12-24 01:41:47 +1100903}
Darren Tucker7b48d252005-02-16 13:20:07 +1100904
905/*
906 * We may attempt to re-seed during mkstemp if we are using the one in the
Darren Tucker7a8619a2005-02-16 13:32:30 +1100907 * compat library (via mkstemp -> _gettemp -> arc4random -> seed_rng) so we
908 * need our own seed_rng(). We must also check that we have enough entropy.
Darren Tucker7b48d252005-02-16 13:20:07 +1100909 */
910void
911seed_rng(void)
912{
913 if (!RAND_status())
914 fatal("Not enough entropy in RNG");
915}