blob: cbb36fd2983c4a3ab56ef76cca0518eac91bb81b [file] [log] [blame]
Damien Miller040f3832000-04-03 14:50:43 +10001/*
2 * Copyright (c) 2000 Damien Miller. All rights reserved.
3 *
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 * 3. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by Markus Friedl.
15 * 4. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "includes.h"
31
32#include "ssh.h"
33#include "xmalloc.h"
34
Damien Miller5f056372000-04-16 12:31:48 +100035#include <openssl/rand.h>
36#include <openssl/sha.h>
Damien Miller040f3832000-04-03 14:50:43 +100037
Damien Miller14c12cb2000-06-07 22:20:23 +100038RCSID("$Id: entropy.c,v 1.13 2000/06/07 12:20:23 djm Exp $");
Damien Miller040f3832000-04-03 14:50:43 +100039
Damien Miller040f3832000-04-03 14:50:43 +100040#ifndef offsetof
41# define offsetof(type, member) ((size_t) &((type *)0)->member)
42#endif
Damien Miller14c12cb2000-06-07 22:20:23 +100043
44/* Print lots of detail */
45/* #define DEBUG_ENTROPY */
46
47/* Number of times to pass through command list gathering entropy */
48#define NUM_ENTROPY_RUNS 1
49
50/* Scale entropy estimates back by this amount on subsequent runs */
51#define SCALE_PER_RUN 10.0
52
53/* Minimum number of commands to be considered valid */
54#define MIN_ENTROPY_SOURCES 16
55
56#define WHITESPACE " \t\n"
57
58#if defined(EGD_SOCKET) || defined(RANDOM_POOL)
59
60#ifdef EGD_SOCKET
Damien Miller040f3832000-04-03 14:50:43 +100061/* Collect entropy from EGD */
62void get_random_bytes(unsigned char *buf, int len)
63{
64 static int egd_socket = -1;
65 int c;
66 char egd_message[2] = { 0x02, 0x00 };
67 struct sockaddr_un addr;
68 int addr_len;
69
70 memset(&addr, '\0', sizeof(addr));
71 addr.sun_family = AF_UNIX;
72
73 /* FIXME: compile time check? */
74 if (sizeof(EGD_SOCKET) > sizeof(addr.sun_path))
75 fatal("Random pool path is too long");
76
Damien Miller14c12cb2000-06-07 22:20:23 +100077 strlcpy(addr.sun_path, EGD_SOCKET, sizeof(addr.sun_path));
Damien Miller040f3832000-04-03 14:50:43 +100078
79 addr_len = offsetof(struct sockaddr_un, sun_path) + sizeof(EGD_SOCKET);
80
81 if (egd_socket == -1) {
82 egd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
83 if (egd_socket == -1)
84 fatal("Couldn't create AF_UNIX socket: %s", strerror(errno));
85 if (connect(egd_socket, (struct sockaddr*)&addr, addr_len) == -1)
86 fatal("Couldn't connect to EGD socket \"%s\": %s", addr.sun_path, strerror(errno));
87 }
88
89 if (len > 255)
90 fatal("Too many bytes to read from EGD");
91
92 /* Send blocking read request to EGD */
93 egd_message[1] = len;
94
95 c = atomicio(write, egd_socket, egd_message, sizeof(egd_message));
96 if (c == -1)
97 fatal("Couldn't write to EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
98
99 c = atomicio(read, egd_socket, buf, len);
100 if (c <= 0)
101 fatal("Couldn't read from EGD socket \"%s\": %s", EGD_SOCKET, strerror(errno));
Damien Miller040f3832000-04-03 14:50:43 +1000102}
103#else /* !EGD_SOCKET */
104#ifdef RANDOM_POOL
105/* Collect entropy from /dev/urandom or pipe */
106void get_random_bytes(unsigned char *buf, int len)
107{
108 static int random_pool = -1;
109 int c;
110
111 if (random_pool == -1) {
112 random_pool = open(RANDOM_POOL, O_RDONLY);
113 if (random_pool == -1)
114 fatal("Couldn't open random pool \"%s\": %s", RANDOM_POOL, strerror(errno));
115 }
116
Damien Miller040f3832000-04-03 14:50:43 +1000117 c = atomicio(read, random_pool, buf, len);
118 if (c <= 0)
119 fatal("Couldn't read from random pool \"%s\": %s", RANDOM_POOL, strerror(errno));
120}
121#endif /* RANDOM_POOL */
122#endif /* EGD_SOCKET */
123
Damien Miller14c12cb2000-06-07 22:20:23 +1000124/*
125 * Seed OpenSSL's random number pool from Kernel random number generator
126 * or EGD
127 */
128void
129seed_rng(void)
130{
131 char buf[32];
132
133 debug("Seeding random number generator");
134 get_random_bytes(buf, sizeof(buf));
135 RAND_add(buf, sizeof(buf), sizeof(buf));
136 memset(buf, '\0', sizeof(buf));
137}
138
139#else /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */
140
Damien Miller040f3832000-04-03 14:50:43 +1000141/*
142 * FIXME: proper entropy estimations. All current values are guesses
Damien Miller4018c192000-04-30 09:30:44 +1000143 * FIXME: (ATL) do estimates at compile time?
Damien Miller040f3832000-04-03 14:50:43 +1000144 * FIXME: More entropy sources
145 */
146
Damien Miller4018c192000-04-30 09:30:44 +1000147/* slow command timeouts (all in milliseconds) */
148/* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
149static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
150
151static int prng_seed_loaded = 0;
Damien Miller0437b332000-05-02 09:56:41 +1000152static int prng_seed_saved = 0;
153static int prng_commands_loaded = 0;
Damien Miller040f3832000-04-03 14:50:43 +1000154
155typedef struct
156{
157 /* Proportion of data that is entropy */
158 double rate;
Damien Miller4018c192000-04-30 09:30:44 +1000159 /* Counter goes positive if this command times out */
160 unsigned int badness;
161 /* Increases by factor of two each timeout */
162 unsigned int sticky_badness;
Damien Miller040f3832000-04-03 14:50:43 +1000163 /* Path to executable */
Damien Miller0437b332000-05-02 09:56:41 +1000164 char *path;
Damien Miller040f3832000-04-03 14:50:43 +1000165 /* argv to pass to executable */
Damien Miller0437b332000-05-02 09:56:41 +1000166 char *args[5];
Damien Miller8d1fd572000-05-17 21:34:07 +1000167 /* full command string (debug) */
168 char *cmdstring;
Damien Miller040f3832000-04-03 14:50:43 +1000169} entropy_source_t;
170
Damien Miller4018c192000-04-30 09:30:44 +1000171double stir_from_system(void);
172double stir_from_programs(void);
173double stir_gettimeofday(double entropy_estimate);
174double stir_clock(double entropy_estimate);
175double stir_rusage(int who, double entropy_estimate);
176double hash_output_from_command(entropy_source_t *src, char *hash);
177
Damien Miller0437b332000-05-02 09:56:41 +1000178/* this is initialised from a file, by prng_read_commands() */
179entropy_source_t *entropy_sources = NULL;
Damien Miller040f3832000-04-03 14:50:43 +1000180
Damien Miller040f3832000-04-03 14:50:43 +1000181double
182stir_from_system(void)
183{
184 double total_entropy_estimate;
185 long int i;
186
187 total_entropy_estimate = 0;
188
189 i = getpid();
190 RAND_add(&i, sizeof(i), 0.1);
191 total_entropy_estimate += 0.1;
192
193 i = getppid();
194 RAND_add(&i, sizeof(i), 0.1);
195 total_entropy_estimate += 0.1;
196
197 i = getuid();
198 RAND_add(&i, sizeof(i), 0.0);
199 i = getgid();
200 RAND_add(&i, sizeof(i), 0.0);
201
202 total_entropy_estimate += stir_gettimeofday(1.0);
203 total_entropy_estimate += stir_clock(0.2);
204 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
205
206 return(total_entropy_estimate);
207}
208
209double
210stir_from_programs(void)
211{
212 int i;
213 int c;
214 double entropy_estimate;
215 double total_entropy_estimate;
216 char hash[SHA_DIGEST_LENGTH];
217
Damien Miller040f3832000-04-03 14:50:43 +1000218 total_entropy_estimate = 0;
Damien Miller14c12cb2000-06-07 22:20:23 +1000219 for(i = 0; i < NUM_ENTROPY_RUNS; i++) {
Damien Miller040f3832000-04-03 14:50:43 +1000220 c = 0;
221 while (entropy_sources[c].path != NULL) {
Damien Miller040f3832000-04-03 14:50:43 +1000222
Damien Miller4018c192000-04-30 09:30:44 +1000223 if (!entropy_sources[c].badness) {
224 /* Hash output from command */
225 entropy_estimate = hash_output_from_command(&entropy_sources[c], hash);
226
227 /* Scale back entropy estimate according to command's rate */
228 entropy_estimate *= entropy_sources[c].rate;
Damien Miller040f3832000-04-03 14:50:43 +1000229
Damien Miller4018c192000-04-30 09:30:44 +1000230 /* Upper bound of entropy estimate is SHA_DIGEST_LENGTH */
231 if (entropy_estimate > SHA_DIGEST_LENGTH)
232 entropy_estimate = SHA_DIGEST_LENGTH;
Damien Miller040f3832000-04-03 14:50:43 +1000233
Damien Miller14c12cb2000-06-07 22:20:23 +1000234 /* Scale back estimates for subsequent passes through list */
235 entropy_estimate /= SCALE_PER_RUN * (i + 1.0);
Damien Miller040f3832000-04-03 14:50:43 +1000236
Damien Miller4018c192000-04-30 09:30:44 +1000237 /* Stir it in */
238 RAND_add(hash, sizeof(hash), entropy_estimate);
Damien Miller040f3832000-04-03 14:50:43 +1000239
Damien Miller14c12cb2000-06-07 22:20:23 +1000240#ifdef DEBUG_ENTROPY
Damien Miller8d1fd572000-05-17 21:34:07 +1000241 debug("Got %0.2f bytes of entropy from '%s'", entropy_estimate,
242 entropy_sources[c].cmdstring);
Damien Miller040f3832000-04-03 14:50:43 +1000243#endif
244
Damien Miller4018c192000-04-30 09:30:44 +1000245 total_entropy_estimate += entropy_estimate;
Damien Miller040f3832000-04-03 14:50:43 +1000246
247 /* Execution times should be a little unpredictable */
Damien Miller4018c192000-04-30 09:30:44 +1000248 total_entropy_estimate += stir_gettimeofday(0.05);
249 total_entropy_estimate += stir_clock(0.05);
250 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 0.1);
251 total_entropy_estimate += stir_rusage(RUSAGE_CHILDREN, 0.1);
252 } else {
Damien Miller14c12cb2000-06-07 22:20:23 +1000253#ifdef DEBUG_ENTROPY
Damien Miller8d1fd572000-05-17 21:34:07 +1000254 debug("Command '%s' disabled (badness %d)",
255 entropy_sources[c].cmdstring, entropy_sources[c].badness);
Damien Miller4018c192000-04-30 09:30:44 +1000256#endif
257
258 if (entropy_sources[c].badness > 0)
259 entropy_sources[c].badness--;
260 }
261
Damien Miller040f3832000-04-03 14:50:43 +1000262 c++;
263 }
264 }
265
266 return(total_entropy_estimate);
267}
268
269double
270stir_gettimeofday(double entropy_estimate)
271{
272 struct timeval tv;
273
274 if (gettimeofday(&tv, NULL) == -1)
275 fatal("Couldn't gettimeofday: %s", strerror(errno));
276
277 RAND_add(&tv, sizeof(tv), entropy_estimate);
278
279 return(entropy_estimate);
280}
281
282double
283stir_clock(double entropy_estimate)
284{
285#ifdef HAVE_CLOCK
286 clock_t c;
287
288 c = clock();
289 RAND_add(&c, sizeof(c), entropy_estimate);
290
291 return(entropy_estimate);
292#else /* _HAVE_CLOCK */
293 return(0);
294#endif /* _HAVE_CLOCK */
295}
296
297double
298stir_rusage(int who, double entropy_estimate)
299{
300#ifdef HAVE_GETRUSAGE
301 struct rusage ru;
302
Damien Miller4018c192000-04-30 09:30:44 +1000303 if (getrusage(who, &ru) == -1)
Damien Miller040f3832000-04-03 14:50:43 +1000304 fatal("Couldn't getrusage: %s", strerror(errno));
305
306 RAND_add(&ru, sizeof(ru), 0.1);
307
308 return(entropy_estimate);
309#else /* _HAVE_GETRUSAGE */
310 return(0);
311#endif /* _HAVE_GETRUSAGE */
312}
313
Damien Miller8d1fd572000-05-17 21:34:07 +1000314
315static
316int
317_get_timeval_msec_difference(struct timeval *t1, struct timeval *t2) {
318 int secdiff, usecdiff;
319
320 secdiff = t2->tv_sec - t1->tv_sec;
321 usecdiff = (secdiff*1000000) + (t2->tv_usec - t1->tv_usec);
322 return (int)(usecdiff / 1000);
323}
324
Damien Miller040f3832000-04-03 14:50:43 +1000325double
Damien Miller4018c192000-04-30 09:30:44 +1000326hash_output_from_command(entropy_source_t *src, char *hash)
Damien Miller040f3832000-04-03 14:50:43 +1000327{
328 static int devnull = -1;
329 int p[2];
Damien Miller4018c192000-04-30 09:30:44 +1000330 fd_set rdset;
331 int cmd_eof = 0, error_abort = 0;
Damien Miller8d1fd572000-05-17 21:34:07 +1000332 struct timeval tv_start, tv_current;
333 int msec_elapsed = 0;
Damien Miller040f3832000-04-03 14:50:43 +1000334 pid_t pid;
335 int status;
Damien Miller8d1fd572000-05-17 21:34:07 +1000336 char buf[16384];
Damien Miller040f3832000-04-03 14:50:43 +1000337 int bytes_read;
338 int total_bytes_read;
339 SHA_CTX sha;
340
341 if (devnull == -1) {
342 devnull = open("/dev/null", O_RDWR);
343 if (devnull == -1)
344 fatal("Couldn't open /dev/null: %s", strerror(errno));
345 }
346
347 if (pipe(p) == -1)
348 fatal("Couldn't open pipe: %s", strerror(errno));
349
Damien Miller8d1fd572000-05-17 21:34:07 +1000350 (void)gettimeofday(&tv_start, NULL); /* record start time */
351
Damien Miller040f3832000-04-03 14:50:43 +1000352 switch (pid = fork()) {
353 case -1: /* Error */
354 close(p[0]);
355 close(p[1]);
356 fatal("Couldn't fork: %s", strerror(errno));
357 /* NOTREACHED */
358 case 0: /* Child */
Damien Miller4018c192000-04-30 09:30:44 +1000359 dup2(devnull, STDIN_FILENO);
360 dup2(p[1], STDOUT_FILENO);
361 dup2(p[1], STDERR_FILENO);
Damien Miller040f3832000-04-03 14:50:43 +1000362 close(p[0]);
363 close(p[1]);
364 close(devnull);
365
Damien Miller4018c192000-04-30 09:30:44 +1000366 execv(src->path, (char**)(src->args));
Damien Miller8d1fd572000-05-17 21:34:07 +1000367 debug("(child) Couldn't exec '%s': %s", src->cmdstring,
368 strerror(errno));
Damien Miller4018c192000-04-30 09:30:44 +1000369 src->badness = src->sticky_badness = 128;
Damien Miller040f3832000-04-03 14:50:43 +1000370 _exit(-1);
371 default: /* Parent */
372 break;
373 }
374
375 RAND_add(&pid, sizeof(&pid), 0.0);
376
377 close(p[1]);
378
379 /* Hash output from child */
380 SHA1_Init(&sha);
381 total_bytes_read = 0;
Damien Miller4018c192000-04-30 09:30:44 +1000382
383 while (!error_abort && !cmd_eof) {
384 int ret;
385 struct timeval tv;
Damien Miller8d1fd572000-05-17 21:34:07 +1000386 int msec_remaining;
387
388 (void) gettimeofday(&tv_current, 0);
Damien Miller14c12cb2000-06-07 22:20:23 +1000389 msec_elapsed = _get_timeval_msec_difference(&tv_start, &tv_current);
Damien Miller8d1fd572000-05-17 21:34:07 +1000390 if (msec_elapsed >= entropy_timeout_current) {
391 error_abort=1;
392 continue;
393 }
394 msec_remaining = entropy_timeout_current - msec_elapsed;
Damien Miller4018c192000-04-30 09:30:44 +1000395
396 FD_ZERO(&rdset);
397 FD_SET(p[0], &rdset);
Damien Miller8d1fd572000-05-17 21:34:07 +1000398 tv.tv_sec = msec_remaining / 1000;
399 tv.tv_usec = (msec_remaining % 1000) * 1000;
Damien Miller4018c192000-04-30 09:30:44 +1000400
401 ret = select(p[0]+1, &rdset, NULL, NULL, &tv);
Damien Miller8d1fd572000-05-17 21:34:07 +1000402
Damien Miller4018c192000-04-30 09:30:44 +1000403 switch (ret) {
404 case 0:
405 /* timer expired */
406 error_abort = 1;
407 break;
408
409 case 1:
410 /* command input */
411 bytes_read = read(p[0], buf, sizeof(buf));
412 if (bytes_read == -1) {
413 error_abort = 1;
414 break;
415 }
Damien Miller8d1fd572000-05-17 21:34:07 +1000416 if (bytes_read) {
417 SHA1_Update(&sha, buf, bytes_read);
418 total_bytes_read += bytes_read;
419 RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
420 } else
421 cmd_eof = 1;
Damien Miller4018c192000-04-30 09:30:44 +1000422
423 break;
424
425 case -1:
426 default:
Damien Miller8d1fd572000-05-17 21:34:07 +1000427 debug("Command '%s': select() failed: %s", src->cmdstring,
428 strerror(errno));
Damien Miller4018c192000-04-30 09:30:44 +1000429 error_abort = 1;
430 break;
431 } /* switch ret */
432
433 RAND_add(&tv, sizeof(&tv), 0.0);
434 } /* while !error_abort && !cmd_eof */
435
Damien Miller040f3832000-04-03 14:50:43 +1000436 SHA1_Final(hash, &sha);
437
438 close(p[0]);
Damien Miller8d1fd572000-05-17 21:34:07 +1000439
Damien Miller14c12cb2000-06-07 22:20:23 +1000440#ifdef DEBUG_ENTROPY
Damien Miller8d1fd572000-05-17 21:34:07 +1000441 debug("Time elapsed: %d msec", msec_elapsed);
Damien Miller14c12cb2000-06-07 22:20:23 +1000442#endif
Damien Miller040f3832000-04-03 14:50:43 +1000443
444 if (waitpid(pid, &status, 0) == -1) {
Damien Miller8d1fd572000-05-17 21:34:07 +1000445 debug("Couldn't wait for child '%s' completion: %s", src->cmdstring,
446 strerror(errno));
Damien Miller4018c192000-04-30 09:30:44 +1000447 return(0.0);
Damien Miller040f3832000-04-03 14:50:43 +1000448 }
449
450 RAND_add(&status, sizeof(&status), 0.0);
451
Damien Miller4018c192000-04-30 09:30:44 +1000452 if (error_abort) {
453 /* closing p[0] on timeout causes the entropy command to
454 * SIGPIPE. Take whatever output we got, and mark this command
455 * as slow */
Damien Miller8d1fd572000-05-17 21:34:07 +1000456 debug("Command '%s' timed out", src->cmdstring);
Damien Miller4018c192000-04-30 09:30:44 +1000457 src->sticky_badness *= 2;
458 src->badness = src->sticky_badness;
Damien Miller040f3832000-04-03 14:50:43 +1000459 return(total_bytes_read);
Damien Miller4018c192000-04-30 09:30:44 +1000460 }
461
462 if (WIFEXITED(status)) {
463 if (WEXITSTATUS(status)==0) {
464 return(total_bytes_read);
465 } else {
Damien Miller14c12cb2000-06-07 22:20:23 +1000466 debug("Command '%s' exit status was %d", src->cmdstring,
467 WEXITSTATUS(status));
Damien Miller4018c192000-04-30 09:30:44 +1000468 src->badness = src->sticky_badness = 128;
469 return (0.0);
470 }
471 } else if (WIFSIGNALED(status)) {
Damien Miller14c12cb2000-06-07 22:20:23 +1000472 debug("Command '%s' returned on uncaught signal %d !", src->cmdstring,
473 status);
Damien Miller4018c192000-04-30 09:30:44 +1000474 src->badness = src->sticky_badness = 128;
475 return(0.0);
476 } else
477 return(0.0);
Damien Miller040f3832000-04-03 14:50:43 +1000478}
Damien Miller4018c192000-04-30 09:30:44 +1000479
480/*
481 * prng seedfile functions
482 */
483int
484prng_check_seedfile(char *filename) {
485
486 struct stat st;
487
488 /* FIXME raceable: eg replace seed between this stat and subsequent open */
489 /* Not such a problem because we don't trust the seed file anyway */
490 if (lstat(filename, &st) == -1) {
491 /* Fail on hard errors */
492 if (errno != ENOENT)
493 fatal("Couldn't stat random seed file \"%s\": %s", filename,
494 strerror(errno));
495
496 return(0);
497 }
498
499 /* regular file? */
500 if (!S_ISREG(st.st_mode))
501 fatal("PRNG seedfile %.100s is not a regular file", filename);
502
503 /* mode 0600, owned by root or the current user? */
Damien Milleraccfeb32000-05-11 19:10:58 +1000504 if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid()))
Damien Miller4018c192000-04-30 09:30:44 +1000505 fatal("PRNG seedfile %.100s must be mode 0600, owned by uid %d",
506 filename, getuid());
507
508 return(1);
509}
510
511void
512prng_write_seedfile(void) {
513 int fd;
514 char seed[1024];
515 char filename[1024];
516 struct passwd *pw;
517
518 /* Don't bother if we have already saved a seed */
519 if (prng_seed_saved)
520 return;
521
Damien Millerfc0b11b2000-05-02 00:03:55 +1000522 prng_seed_saved = 1;
523
Damien Miller4018c192000-04-30 09:30:44 +1000524 pw = getpwuid(getuid());
525 if (pw == NULL)
526 fatal("Couldn't get password entry for current user (%i): %s",
527 getuid(), strerror(errno));
528
529 /* Try to ensure that the parent directory is there */
530 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
531 SSH_USER_DIR);
532 mkdir(filename, 0700);
533
534 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
535 SSH_PRNG_SEED_FILE);
536
537 debug("writing PRNG seed to file %.100s", filename);
538
539 RAND_bytes(seed, sizeof(seed));
540
541 /* Don't care if the seed doesn't exist */
542 prng_check_seedfile(filename);
543
544 if ((fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600)) == -1)
545 fatal("couldn't access PRNG seedfile %.100s (%.100s)", filename,
546 strerror(errno));
547
548 if (atomicio(write, fd, &seed, sizeof(seed)) != sizeof(seed))
549 fatal("problem writing PRNG seedfile %.100s (%.100s)", filename,
550 strerror(errno));
551
552 close(fd);
553}
554
555void
556prng_read_seedfile(void) {
557 int fd;
558 char seed[1024];
559 char filename[1024];
560 struct passwd *pw;
561
562 pw = getpwuid(getuid());
563 if (pw == NULL)
564 fatal("Couldn't get password entry for current user (%i): %s",
565 getuid(), strerror(errno));
566
567 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
568 SSH_PRNG_SEED_FILE);
569
570 debug("loading PRNG seed from file %.100s", filename);
571
572 if (!prng_check_seedfile(filename)) {
573 verbose("Random seed file not found, creating new");
574 prng_write_seedfile();
575
576 /* Reseed immediatly */
577 (void)stir_from_system();
578 (void)stir_from_programs();
579 return;
580 }
581
582 /* open the file and read in the seed */
583 fd = open(filename, O_RDONLY);
584 if (fd == -1)
585 fatal("could not open PRNG seedfile %.100s (%.100s)", filename,
586 strerror(errno));
587
588 if (atomicio(read, fd, &seed, sizeof(seed)) != sizeof(seed)) {
589 verbose("invalid or short read from PRNG seedfile %.100s - ignoring",
590 filename);
591 memset(seed, '\0', sizeof(seed));
592 }
593 close(fd);
594
595 /* stir in the seed, with estimated entropy zero */
596 RAND_add(&seed, sizeof(seed), 0.0);
597}
598
Damien Miller0437b332000-05-02 09:56:41 +1000599
600/*
601 * entropy command initialisation functions
602 */
Damien Miller0437b332000-05-02 09:56:41 +1000603int
604prng_read_commands(char *cmdfilename)
605{
606 FILE *f;
Damien Miller0437b332000-05-02 09:56:41 +1000607 char *cp;
Damien Miller14c12cb2000-06-07 22:20:23 +1000608 char line[1024];
609 char cmd[1024];
610 char path[256];
Damien Miller0437b332000-05-02 09:56:41 +1000611 int linenum;
Damien Miller0437b332000-05-02 09:56:41 +1000612 int num_cmds = 64;
613 int cur_cmd = 0;
Damien Miller14c12cb2000-06-07 22:20:23 +1000614 double est;
615 entropy_source_t *entcmd;
Damien Miller0437b332000-05-02 09:56:41 +1000616
617 f = fopen(cmdfilename, "r");
618 if (!f) {
619 fatal("couldn't read entropy commands file %.100s: %.100s",
620 cmdfilename, strerror(errno));
621 }
622
Damien Miller0437b332000-05-02 09:56:41 +1000623 entcmd = (entropy_source_t *)xmalloc(num_cmds * sizeof(entropy_source_t));
624 memset(entcmd, '\0', num_cmds * sizeof(entropy_source_t));
625
Damien Miller14c12cb2000-06-07 22:20:23 +1000626 /* Read in file */
627 linenum = 0;
Damien Miller0437b332000-05-02 09:56:41 +1000628 while (fgets(line, sizeof(line), f)) {
Damien Miller14c12cb2000-06-07 22:20:23 +1000629 int arg;
630 char *argv;
631
Damien Miller0437b332000-05-02 09:56:41 +1000632 linenum++;
633
634 /* skip leading whitespace, test for blank line or comment */
635 cp = line + strspn(line, WHITESPACE);
636 if ((*cp == 0) || (*cp == '#'))
637 continue; /* done with this line */
638
Damien Miller14c12cb2000-06-07 22:20:23 +1000639 /* First non-whitespace char should be double quote delimiting */
640 /* commandline */
641 if (*cp != '"') {
Damien Miller0437b332000-05-02 09:56:41 +1000642 error("bad entropy command, %.100s line %d", cmdfilename,
643 linenum);
644 continue;
Damien Miller14c12cb2000-06-07 22:20:23 +1000645 }
646
647 /* first token, command args (incl. argv[0]) in double quotes */
648 cp = strtok(cp, "\"");
649 if (cp == NULL) {
650 error("missing or bad command string, %.100s line %d -- ignored",
651 cmdfilename, linenum);
652 continue;
653 }
654 strlcpy(cmd, cp, sizeof(cmd));
655
656 /* second token, full command path */
657 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
658 error("missing command path, %.100s line %d -- ignored",
659 cmdfilename, linenum);
660 continue;
661 }
662
663 /* did configure mark this as dead? */
664 if (strncmp("undef", cp, 5) == 0)
665 continue;
666
667 strlcpy(path, cp, sizeof(path));
668
669 /* third token, entropy rate estimate for this command */
670 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
671 error("missing entropy estimate, %.100s line %d -- ignored",
672 cmdfilename, linenum);
673 continue;
674 }
675 est = strtod(cp, &argv);
676
677 /* end of line */
678 if ((cp = strtok(NULL, WHITESPACE)) != NULL) {
679 error("garbage at end of line %d in %.100s -- ignored", linenum,
680 cmdfilename);
681 continue;
682 }
683
684 /* save the command for debug messages */
685 entcmd[cur_cmd].cmdstring = xstrdup(cmd);
686
687 /* split the command args */
688 cp = strtok(cmd, WHITESPACE);
689 arg = 0;
690 argv = NULL;
691 do {
692 char *s = (char*)xmalloc(strlen(cp) + 1);
693 strncpy(s, cp, strlen(cp) + 1);
694 entcmd[cur_cmd].args[arg] = s;
695 arg++;
696 } while ((arg < 5) && (cp = strtok(NULL, WHITESPACE)));
697
698 if (strtok(NULL, WHITESPACE))
699 error("ignored extra command elements (max 5), %.100s line %d",
700 cmdfilename, linenum);
701
702 /* Copy the command path and rate estimate */
703 entcmd[cur_cmd].path = xstrdup(path);
704 entcmd[cur_cmd].rate = est;
705
706 /* Initialise other values */
707 entcmd[cur_cmd].sticky_badness = 1;
708
709 cur_cmd++;
710
711 /* If we've filled the array, reallocate it twice the size */
712 /* Do this now because even if this we're on the last command,
713 we need another slot to mark the last entry */
714 if (cur_cmd == num_cmds) {
715 num_cmds *= 2;
716 entcmd = xrealloc(entcmd, num_cmds * sizeof(entropy_source_t));
Damien Miller0437b332000-05-02 09:56:41 +1000717 }
718 }
719
720 /* zero the last entry */
721 memset(&entcmd[cur_cmd], '\0', sizeof(entropy_source_t));
Damien Miller14c12cb2000-06-07 22:20:23 +1000722
Damien Miller0437b332000-05-02 09:56:41 +1000723 /* trim to size */
724 entropy_sources = xrealloc(entcmd, (cur_cmd+1) * sizeof(entropy_source_t));
725
726 debug("loaded %d entropy commands from %.100s", cur_cmd, cmdfilename);
727
728 return (cur_cmd >= MIN_ENTROPY_SOURCES);
729}
730
Damien Miller040f3832000-04-03 14:50:43 +1000731/*
Damien Miller4018c192000-04-30 09:30:44 +1000732 * Write a keyfile at exit
733 */
734void
735prng_seed_cleanup(void *junk)
736{
737 prng_write_seedfile();
738}
739
740/*
741 * Conditionally Seed OpenSSL's random number pool from
742 * syscalls and program output
Damien Miller040f3832000-04-03 14:50:43 +1000743 */
744void
745seed_rng(void)
746{
Damien Millerf3c6cf12000-05-17 22:08:29 +1000747 void *old_sigchld_handler;
748
Damien Miller0437b332000-05-02 09:56:41 +1000749 if (!prng_commands_loaded) {
750 if (!prng_read_commands(SSH_PRNG_COMMAND_FILE))
751 fatal("PRNG initialisation failed -- exiting.");
752 prng_commands_loaded = 1;
753 }
754
Damien Millerf3c6cf12000-05-17 22:08:29 +1000755 /* Make sure some other sigchld handler doesn't reap our entropy */
756 /* commands */
757 old_sigchld_handler = signal(SIGCHLD, SIG_DFL);
758
Damien Miller74a333b2000-04-04 15:04:09 +1000759 debug("Seeding random number generator.");
760 debug("OpenSSL random status is now %i\n", RAND_status());
761 debug("%i bytes from system calls", (int)stir_from_system());
762 debug("%i bytes from programs", (int)stir_from_programs());
763 debug("OpenSSL random status is now %i\n", RAND_status());
Damien Miller4018c192000-04-30 09:30:44 +1000764
Damien Millerf3c6cf12000-05-17 22:08:29 +1000765 signal(SIGCHLD, old_sigchld_handler);
766
Damien Miller8d1fd572000-05-17 21:34:07 +1000767 if (!RAND_status())
768 fatal("Couldn't initialise builtin random number generator -- exiting.");
769
Damien Miller4018c192000-04-30 09:30:44 +1000770 if (!prng_seed_loaded)
771 {
772 prng_seed_loaded = 1;
773 prng_seed_saved = 0;
774 prng_read_seedfile();
775 fatal_add_cleanup(prng_seed_cleanup, NULL);
776 atexit(prng_write_seedfile);
777 }
Damien Miller040f3832000-04-03 14:50:43 +1000778}
779#endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */