summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2002-01-21 23:44:12 +1100
committerDamien Miller <djm@mindrot.org>2002-01-21 23:44:12 +1100
commit7b10ef48771bc3649b6e5ea0b021a2270a5d62f8 (patch)
tree98d9fb3fa5718fccbb88903a812beae3e989507d
parenta234451a706f091e1f1f60203ade0dca3d7e99b6 (diff)
- (djm) Rework ssh-rand-helper:
- Reduce quantity of ifdef code, in preparation for ssh_rand_conf - Always seed from system calls, even when doing PRNGd seeding - Tidy and comment #define knobs - Remove unused facility for multiple runs through command list - KNF, cleanup, update copyright
-rw-r--r--ChangeLog10
-rw-r--r--ssh-rand-helper.c697
2 files changed, 358 insertions, 349 deletions
diff --git a/ChangeLog b/ChangeLog
index 70e50f786..731337317 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
120020121
2 - (djm) Rework ssh-rand-helper:
3 - Reduce quantity of ifdef code, in preparation for ssh_rand_conf
4 - Always seed from system calls, even when doing PRNGd seeding
5 - Tidy and comment #define knobs
6 - Remove unused facility for multiple runs through command list
7 - KNF, cleanup, update copyright
8
120020114 920020114
2 - (djm) Bug #50 - make autoconf entropy path checks more robust 10 - (djm) Bug #50 - make autoconf entropy path checks more robust
3 11
@@ -7136,4 +7144,4 @@
7136 - Wrote replacements for strlcpy and mkdtemp 7144 - Wrote replacements for strlcpy and mkdtemp
7137 - Released 1.0pre1 7145 - Released 1.0pre1
7138 7146
7139$Id: ChangeLog,v 1.1720 2002/01/14 08:01:03 djm Exp $ 7147$Id: ChangeLog,v 1.1721 2002/01/21 12:44:12 djm Exp $
diff --git a/ssh-rand-helper.c b/ssh-rand-helper.c
index 380fee46d..596622b42 100644
--- a/ssh-rand-helper.c
+++ b/ssh-rand-helper.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 2001 Damien Miller. All rights reserved. 2 * Copyright (c) 2001-2002 Damien Miller. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -39,37 +39,41 @@
39#include "pathnames.h" 39#include "pathnames.h"
40#include "log.h" 40#include "log.h"
41 41
42#ifdef HAVE___PROGNAME 42RCSID("$Id: ssh-rand-helper.c,v 1.3 2002/01/21 12:44:12 djm Exp $");
43extern char *__progname;
44#else
45char *__progname;
46#endif
47 43
48RCSID("$Id: ssh-rand-helper.c,v 1.2 2001/12/25 04:32:58 stevesk Exp $"); 44/* Number of bytes we write out */
45#define OUTPUT_SEED_SIZE 48
49 46
50#define RANDOM_SEED_SIZE 48 47/* Length of on-disk seedfiles */
48#define SEED_FILE_SIZE 1024
51 49
50/* Maximum number of command-line arguments to read from file */
51#define NUM_ARGS 10
52
53/* Minimum number of usable commands to be considered sufficient */
54#define MIN_ENTROPY_SOURCES 16
55
56/* Path to on-disk seed file (relative to user's home directory */
52#ifndef SSH_PRNG_SEED_FILE 57#ifndef SSH_PRNG_SEED_FILE
53# define SSH_PRNG_SEED_FILE _PATH_SSH_USER_DIR"/prng_seed" 58# define SSH_PRNG_SEED_FILE _PATH_SSH_USER_DIR"/prng_seed"
54#endif /* SSH_PRNG_SEED_FILE */ 59#endif
60
61/* Path to PRNG commands list */
55#ifndef SSH_PRNG_COMMAND_FILE 62#ifndef SSH_PRNG_COMMAND_FILE
56# define SSH_PRNG_COMMAND_FILE ETCDIR "/ssh_prng_cmds" 63# define SSH_PRNG_COMMAND_FILE ETCDIR "/ssh_prng_cmds"
57#endif /* SSH_PRNG_COMMAND_FILE */ 64#endif
65
58 66
67#ifdef HAVE___PROGNAME
68extern char *__progname;
69#else
70char *__progname;
71#endif
59 72
60#ifndef offsetof 73#ifndef offsetof
61# define offsetof(type, member) ((size_t) &((type *)0)->member) 74# define offsetof(type, member) ((size_t) &((type *)0)->member)
62#endif 75#endif
63 76
64/* Number of times to pass through command list gathering entropy */
65#define NUM_ENTROPY_RUNS 1
66
67/* Scale entropy estimates back by this amount on subsequent runs */
68#define SCALE_PER_RUN 10.0
69
70/* Minimum number of commands to be considered valid */
71#define MIN_ENTROPY_SOURCES 16
72
73#define WHITESPACE " \t\n" 77#define WHITESPACE " \t\n"
74 78
75#ifndef RUSAGE_SELF 79#ifndef RUSAGE_SELF
@@ -81,70 +85,104 @@ RCSID("$Id: ssh-rand-helper.c,v 1.2 2001/12/25 04:32:58 stevesk Exp $");
81 85
82#if defined(PRNGD_SOCKET) || defined(PRNGD_PORT) 86#if defined(PRNGD_SOCKET) || defined(PRNGD_PORT)
83# define USE_PRNGD 87# define USE_PRNGD
88#else
89# define USE_SEED_FILES
84#endif 90#endif
85 91
86#ifdef USE_PRNGD 92typedef struct {
87/* Collect entropy from PRNGD/EGD */ 93 /* Proportion of data that is entropy */
94 double rate;
95 /* Counter goes positive if this command times out */
96 unsigned int badness;
97 /* Increases by factor of two each timeout */
98 unsigned int sticky_badness;
99 /* Path to executable */
100 char *path;
101 /* argv to pass to executable */
102 char *args[NUM_ARGS]; /* XXX: arbitrary limit */
103 /* full command string (debug) */
104 char *cmdstring;
105} entropy_cmd_t;
106
107/* slow command timeouts (all in milliseconds) */
108/* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
109static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
110
111/* this is initialised from a file, by prng_read_commands() */
112static entropy_cmd_t *entropy_cmds = NULL;
113
114/* Prototypes */
115double stir_from_system(void);
116double stir_from_programs(void);
117double stir_gettimeofday(double entropy_estimate);
118double stir_clock(double entropy_estimate);
119double stir_rusage(int who, double entropy_estimate);
120double hash_command_output(entropy_cmd_t *src, char *hash);
121int get_random_bytes_prngd(unsigned char *buf, int len,
122 unsigned short tcp_port, char *socket_path);
123
124/*
125 * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon
126 * listening either on 'tcp_port', or via Unix domain socket at *
127 * 'socket_path'.
128 * Either a non-zero tcp_port or a non-null socket_path must be
129 * supplied.
130 * Returns 0 on success, -1 on error
131 */
88int 132int
89get_random_bytes(unsigned char *buf, int len) 133get_random_bytes_prngd(unsigned char *buf, int len,
134 unsigned short tcp_port, char *socket_path)
90{ 135{
91 int fd; 136 int fd, addr_len, rval, errors;
92 char msg[2]; 137 char msg[2];
93#ifdef PRNGD_PORT 138 struct sockaddr_storage addr;
94 struct sockaddr_in addr; 139 struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
95#else 140 struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr;
96 struct sockaddr_un addr;
97#endif
98 int addr_len, rval, errors;
99 mysig_t old_sigpipe; 141 mysig_t old_sigpipe;
100 142
143 /* Sanity checks */
144 if (socket_path == NULL && tcp_port == 0)
145 fatal("You must specify a port or a socket");
146 if (socket_path != NULL &&
147 strlen(socket_path) >= sizeof(addr_un->sun_path))
148 fatal("Random pool path is too long");
101 if (len > 255) 149 if (len > 255)
102 fatal("Too many bytes to read from PRNGD"); 150 fatal("Too many bytes to read from PRNGD");
103 151
104 memset(&addr, '\0', sizeof(addr)); 152 memset(&addr, '\0', sizeof(addr));
105 153
106#ifdef PRNGD_PORT 154 if (tcp_port != 0) {
107 addr.sin_family = AF_INET; 155 addr_in->sin_family = AF_INET;
108 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 156 addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
109 addr.sin_port = htons(PRNGD_PORT); 157 addr_in->sin_port = htons(tcp_port);
110 addr_len = sizeof(struct sockaddr_in); 158 addr_len = sizeof(*addr_in);
111#else /* use IP socket PRNGD_SOCKET instead */ 159 } else {
112 /* Sanity checks */ 160 addr_un->sun_family = AF_UNIX;
113 if (sizeof(PRNGD_SOCKET) > sizeof(addr.sun_path)) 161 strlcpy(addr_un->sun_path, socket_path,
114 fatal("Random pool path is too long"); 162 sizeof(addr_un->sun_path));
115 163 addr_len = offsetof(struct sockaddr_un, sun_path) +
116 addr.sun_family = AF_UNIX; 164 strlen(socket_path) + 1;
117 strlcpy(addr.sun_path, PRNGD_SOCKET, sizeof(addr.sun_path)); 165 }
118 addr_len = offsetof(struct sockaddr_un, sun_path) +
119 sizeof(PRNGD_SOCKET);
120#endif
121 166
122 old_sigpipe = mysignal(SIGPIPE, SIG_IGN); 167 old_sigpipe = mysignal(SIGPIPE, SIG_IGN);
123 168
124 errors = rval = 0; 169 errors = 0;
170 rval = -1;
125reopen: 171reopen:
126#ifdef PRNGD_PORT 172 fd = socket(addr.ss_family, SOCK_STREAM, 0);
127 fd = socket(addr.sin_family, SOCK_STREAM, 0);
128 if (fd == -1) {
129 error("Couldn't create AF_INET socket: %s", strerror(errno));
130 goto done;
131 }
132#else
133 fd = socket(addr.sun_family, SOCK_STREAM, 0);
134 if (fd == -1) { 173 if (fd == -1) {
135 error("Couldn't create AF_UNIX socket: %s", strerror(errno)); 174 error("Couldn't create socket: %s", strerror(errno));
136 goto done; 175 goto done;
137 } 176 }
138#endif
139 177
140 if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) { 178 if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
141#ifdef PRNGD_PORT 179 if (tcp_port != 0) {
142 error("Couldn't connect to PRNGD port %d: %s", 180 error("Couldn't connect to PRNGD port %d: %s",
143 PRNGD_PORT, strerror(errno)); 181 tcp_port, strerror(errno));
144#else 182 } else {
145 error("Couldn't connect to PRNGD socket \"%s\": %s", 183 error("Couldn't connect to PRNGD socket \"%s\": %s",
146 addr.sun_path, strerror(errno)); 184 addr_un->sun_path, strerror(errno));
147#endif 185 }
148 goto done; 186 goto done;
149 } 187 }
150 188
@@ -174,146 +212,12 @@ reopen:
174 goto done; 212 goto done;
175 } 213 }
176 214
177 rval = 1; 215 rval = 0;
178done: 216done:
179 mysignal(SIGPIPE, old_sigpipe); 217 mysignal(SIGPIPE, old_sigpipe);
180 if (fd != -1) 218 if (fd != -1)
181 close(fd); 219 close(fd);
182 return(rval); 220 return rval;
183}
184
185static void
186seed_openssl_rng(void)
187{
188 unsigned char buf[RANDOM_SEED_SIZE];
189
190 if (!get_random_bytes(buf, sizeof(buf)))
191 fatal("Entropy collection failed");
192
193 RAND_add(buf, sizeof(buf), sizeof(buf));
194 memset(buf, '\0', sizeof(buf));
195}
196
197#else /* USE_PRNGD */
198
199/*
200 * FIXME: proper entropy estimations. All current values are guesses
201 * FIXME: (ATL) do estimates at compile time?
202 * FIXME: More entropy sources
203 */
204
205/* slow command timeouts (all in milliseconds) */
206/* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
207static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
208
209typedef struct
210{
211 /* Proportion of data that is entropy */
212 double rate;
213 /* Counter goes positive if this command times out */
214 unsigned int badness;
215 /* Increases by factor of two each timeout */
216 unsigned int sticky_badness;
217 /* Path to executable */
218 char *path;
219 /* argv to pass to executable */
220 char *args[5];
221 /* full command string (debug) */
222 char *cmdstring;
223} entropy_source_t;
224
225double stir_from_system(void);
226double stir_from_programs(void);
227double stir_gettimeofday(double entropy_estimate);
228double stir_clock(double entropy_estimate);
229double stir_rusage(int who, double entropy_estimate);
230double hash_output_from_command(entropy_source_t *src, char *hash);
231
232/* this is initialised from a file, by prng_read_commands() */
233entropy_source_t *entropy_sources = NULL;
234
235double
236stir_from_system(void)
237{
238 double total_entropy_estimate;
239 long int i;
240
241 total_entropy_estimate = 0;
242
243 i = getpid();
244 RAND_add(&i, sizeof(i), 0.5);
245 total_entropy_estimate += 0.1;
246
247 i = getppid();
248 RAND_add(&i, sizeof(i), 0.5);
249 total_entropy_estimate += 0.1;
250
251 i = getuid();
252 RAND_add(&i, sizeof(i), 0.0);
253 i = getgid();
254 RAND_add(&i, sizeof(i), 0.0);
255
256 total_entropy_estimate += stir_gettimeofday(1.0);
257 total_entropy_estimate += stir_clock(0.5);
258 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
259
260 return(total_entropy_estimate);
261}
262
263double
264stir_from_programs(void)
265{
266 int i;
267 int c;
268 double entropy_estimate;
269 double total_entropy_estimate;
270 char hash[SHA_DIGEST_LENGTH];
271
272 total_entropy_estimate = 0;
273 for(i = 0; i < NUM_ENTROPY_RUNS; i++) {
274 c = 0;
275 while (entropy_sources[c].path != NULL) {
276
277 if (!entropy_sources[c].badness) {
278 /* Hash output from command */
279 entropy_estimate = hash_output_from_command(&entropy_sources[c], hash);
280
281 /* Scale back entropy estimate according to command's rate */
282 entropy_estimate *= entropy_sources[c].rate;
283
284 /* Upper bound of entropy estimate is SHA_DIGEST_LENGTH */
285 if (entropy_estimate > SHA_DIGEST_LENGTH)
286 entropy_estimate = SHA_DIGEST_LENGTH;
287
288 /* Scale back estimates for subsequent passes through list */
289 entropy_estimate /= SCALE_PER_RUN * (i + 1.0);
290
291 /* Stir it in */
292 RAND_add(hash, sizeof(hash), entropy_estimate);
293
294 debug3("Got %0.2f bytes of entropy from '%s'", entropy_estimate,
295 entropy_sources[c].cmdstring);
296
297 total_entropy_estimate += entropy_estimate;
298
299 /* Execution times should be a little unpredictable */
300 total_entropy_estimate += stir_gettimeofday(0.05);
301 total_entropy_estimate += stir_clock(0.05);
302 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 0.1);
303 total_entropy_estimate += stir_rusage(RUSAGE_CHILDREN, 0.1);
304 } else {
305 debug2("Command '%s' disabled (badness %d)",
306 entropy_sources[c].cmdstring, entropy_sources[c].badness);
307
308 if (entropy_sources[c].badness > 0)
309 entropy_sources[c].badness--;
310 }
311
312 c++;
313 }
314 }
315
316 return(total_entropy_estimate);
317} 221}
318 222
319double 223double
@@ -326,7 +230,7 @@ stir_gettimeofday(double entropy_estimate)
326 230
327 RAND_add(&tv, sizeof(tv), entropy_estimate); 231 RAND_add(&tv, sizeof(tv), entropy_estimate);
328 232
329 return(entropy_estimate); 233 return entropy_estimate;
330} 234}
331 235
332double 236double
@@ -338,9 +242,9 @@ stir_clock(double entropy_estimate)
338 c = clock(); 242 c = clock();
339 RAND_add(&c, sizeof(c), entropy_estimate); 243 RAND_add(&c, sizeof(c), entropy_estimate);
340 244
341 return(entropy_estimate); 245 return entropy_estimate;
342#else /* _HAVE_CLOCK */ 246#else /* _HAVE_CLOCK */
343 return(0); 247 return 0;
344#endif /* _HAVE_CLOCK */ 248#endif /* _HAVE_CLOCK */
345} 249}
346 250
@@ -351,19 +255,19 @@ stir_rusage(int who, double entropy_estimate)
351 struct rusage ru; 255 struct rusage ru;
352 256
353 if (getrusage(who, &ru) == -1) 257 if (getrusage(who, &ru) == -1)
354 return(0); 258 return 0;
355 259
356 RAND_add(&ru, sizeof(ru), entropy_estimate); 260 RAND_add(&ru, sizeof(ru), entropy_estimate);
357 261
358 return(entropy_estimate); 262 return entropy_estimate;
359#else /* _HAVE_GETRUSAGE */ 263#else /* _HAVE_GETRUSAGE */
360 return(0); 264 return 0;
361#endif /* _HAVE_GETRUSAGE */ 265#endif /* _HAVE_GETRUSAGE */
362} 266}
363 267
364
365static int 268static int
366_get_timeval_msec_difference(struct timeval *t1, struct timeval *t2) { 269timeval_diff(struct timeval *t1, struct timeval *t2)
270{
367 int secdiff, usecdiff; 271 int secdiff, usecdiff;
368 272
369 secdiff = t2->tv_sec - t1->tv_sec; 273 secdiff = t2->tv_sec - t1->tv_sec;
@@ -372,27 +276,24 @@ _get_timeval_msec_difference(struct timeval *t1, struct timeval *t2) {
372} 276}
373 277
374double 278double
375hash_output_from_command(entropy_source_t *src, char *hash) 279hash_command_output(entropy_cmd_t *src, char *hash)
376{ 280{
377 static int devnull = -1; 281 char buf[8192];
378 int p[2];
379 fd_set rdset; 282 fd_set rdset;
380 int cmd_eof = 0, error_abort = 0; 283 int bytes_read, cmd_eof, error_abort, msec_elapsed, p[2];
381 struct timeval tv_start, tv_current; 284 int status, total_bytes_read;
382 int msec_elapsed = 0; 285 static int devnull = -1;
383 pid_t pid; 286 pid_t pid;
384 int status;
385 char buf[16384];
386 int bytes_read;
387 int total_bytes_read;
388 SHA_CTX sha; 287 SHA_CTX sha;
288 struct timeval tv_start, tv_current;
389 289
390 debug3("Reading output from \'%s\'", src->cmdstring); 290 debug3("Reading output from \'%s\'", src->cmdstring);
391 291
392 if (devnull == -1) { 292 if (devnull == -1) {
393 devnull = open("/dev/null", O_RDWR); 293 devnull = open("/dev/null", O_RDWR);
394 if (devnull == -1) 294 if (devnull == -1)
395 fatal("Couldn't open /dev/null: %s", strerror(errno)); 295 fatal("Couldn't open /dev/null: %s",
296 strerror(errno));
396 } 297 }
397 298
398 if (pipe(p) == -1) 299 if (pipe(p) == -1)
@@ -415,8 +316,9 @@ hash_output_from_command(entropy_source_t *src, char *hash)
415 close(devnull); 316 close(devnull);
416 317
417 execv(src->path, (char**)(src->args)); 318 execv(src->path, (char**)(src->args));
418 debug("(child) Couldn't exec '%s': %s", src->cmdstring, 319
419 strerror(errno)); 320 debug("(child) Couldn't exec '%s': %s",
321 src->cmdstring, strerror(errno));
420 _exit(-1); 322 _exit(-1);
421 default: /* Parent */ 323 default: /* Parent */
422 break; 324 break;
@@ -428,15 +330,15 @@ hash_output_from_command(entropy_source_t *src, char *hash)
428 330
429 /* Hash output from child */ 331 /* Hash output from child */
430 SHA1_Init(&sha); 332 SHA1_Init(&sha);
431 total_bytes_read = 0;
432 333
334 cmd_eof = error_abort = msec_elapsed = total_bytes_read = 0;
433 while (!error_abort && !cmd_eof) { 335 while (!error_abort && !cmd_eof) {
434 int ret; 336 int ret;
435 struct timeval tv; 337 struct timeval tv;
436 int msec_remaining; 338 int msec_remaining;
437 339
438 (void) gettimeofday(&tv_current, 0); 340 (void) gettimeofday(&tv_current, 0);
439 msec_elapsed = _get_timeval_msec_difference(&tv_start, &tv_current); 341 msec_elapsed = timeval_diff(&tv_start, &tv_current);
440 if (msec_elapsed >= entropy_timeout_current) { 342 if (msec_elapsed >= entropy_timeout_current) {
441 error_abort=1; 343 error_abort=1;
442 continue; 344 continue;
@@ -445,10 +347,10 @@ hash_output_from_command(entropy_source_t *src, char *hash)
445 347
446 FD_ZERO(&rdset); 348 FD_ZERO(&rdset);
447 FD_SET(p[0], &rdset); 349 FD_SET(p[0], &rdset);
448 tv.tv_sec = msec_remaining / 1000; 350 tv.tv_sec = msec_remaining / 1000;
449 tv.tv_usec = (msec_remaining % 1000) * 1000; 351 tv.tv_usec = (msec_remaining % 1000) * 1000;
450 352
451 ret = select(p[0]+1, &rdset, NULL, NULL, &tv); 353 ret = select(p[0] + 1, &rdset, NULL, NULL, &tv);
452 354
453 RAND_add(&tv, sizeof(tv), 0.0); 355 RAND_add(&tv, sizeof(tv), 0.0);
454 356
@@ -476,8 +378,8 @@ hash_output_from_command(entropy_source_t *src, char *hash)
476 case -1: 378 case -1:
477 default: 379 default:
478 /* error */ 380 /* error */
479 debug("Command '%s': select() failed: %s", src->cmdstring, 381 debug("Command '%s': select() failed: %s",
480 strerror(errno)); 382 src->cmdstring, strerror(errno));
481 error_abort = 1; 383 error_abort = 1;
482 break; 384 break;
483 } 385 }
@@ -490,93 +392,174 @@ hash_output_from_command(entropy_source_t *src, char *hash)
490 debug3("Time elapsed: %d msec", msec_elapsed); 392 debug3("Time elapsed: %d msec", msec_elapsed);
491 393
492 if (waitpid(pid, &status, 0) == -1) { 394 if (waitpid(pid, &status, 0) == -1) {
493 error("Couldn't wait for child '%s' completion: %s", src->cmdstring, 395 error("Couldn't wait for child '%s' completion: %s",
494 strerror(errno)); 396 src->cmdstring, strerror(errno));
495 return(0.0); 397 return 0.0;
496 } 398 }
497 399
498 RAND_add(&status, sizeof(&status), 0.0); 400 RAND_add(&status, sizeof(&status), 0.0);
499 401
500 if (error_abort) { 402 if (error_abort) {
501 /* closing p[0] on timeout causes the entropy command to 403 /*
502 * SIGPIPE. Take whatever output we got, and mark this command 404 * Closing p[0] on timeout causes the entropy command to
503 * as slow */ 405 * SIGPIPE. Take whatever output we got, and mark this
406 * command as slow
407 */
504 debug2("Command '%s' timed out", src->cmdstring); 408 debug2("Command '%s' timed out", src->cmdstring);
505 src->sticky_badness *= 2; 409 src->sticky_badness *= 2;
506 src->badness = src->sticky_badness; 410 src->badness = src->sticky_badness;
507 return(total_bytes_read); 411 return total_bytes_read;
508 } 412 }
509 413
510 if (WIFEXITED(status)) { 414 if (WIFEXITED(status)) {
511 if (WEXITSTATUS(status)==0) { 415 if (WEXITSTATUS(status) == 0) {
512 return(total_bytes_read); 416 return total_bytes_read;
513 } else { 417 } else {
514 debug2("Command '%s' exit status was %d", src->cmdstring, 418 debug2("Command '%s' exit status was %d",
515 WEXITSTATUS(status)); 419 src->cmdstring, WEXITSTATUS(status));
516 src->badness = src->sticky_badness = 128; 420 src->badness = src->sticky_badness = 128;
517 return (0.0); 421 return 0.0;
518 } 422 }
519 } else if (WIFSIGNALED(status)) { 423 } else if (WIFSIGNALED(status)) {
520 debug2("Command '%s' returned on uncaught signal %d !", src->cmdstring, 424 debug2("Command '%s' returned on uncaught signal %d !",
521 status); 425 src->cmdstring, status);
522 src->badness = src->sticky_badness = 128; 426 src->badness = src->sticky_badness = 128;
523 return(0.0); 427 return 0.0;
524 } else 428 } else
525 return(0.0); 429 return 0.0;
430}
431
432double
433stir_from_system(void)
434{
435 double total_entropy_estimate;
436 long int i;
437
438 total_entropy_estimate = 0;
439
440 i = getpid();
441 RAND_add(&i, sizeof(i), 0.5);
442 total_entropy_estimate += 0.1;
443
444 i = getppid();
445 RAND_add(&i, sizeof(i), 0.5);
446 total_entropy_estimate += 0.1;
447
448 i = getuid();
449 RAND_add(&i, sizeof(i), 0.0);
450 i = getgid();
451 RAND_add(&i, sizeof(i), 0.0);
452
453 total_entropy_estimate += stir_gettimeofday(1.0);
454 total_entropy_estimate += stir_clock(0.5);
455 total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
456
457 return total_entropy_estimate;
458}
459
460double
461stir_from_programs(void)
462{
463 int c;
464 double entropy, total_entropy;
465 char hash[SHA_DIGEST_LENGTH];
466
467 total_entropy = 0;
468 for(c = 0; entropy_cmds[c].path != NULL; c++) {
469 if (!entropy_cmds[c].badness) {
470 /* Hash output from command */
471 entropy = hash_command_output(&entropy_cmds[c],
472 hash);
473
474 /* Scale back estimate by command's rate */
475 entropy *= entropy_cmds[c].rate;
476
477 /* Upper bound of entropy is SHA_DIGEST_LENGTH */
478 if (entropy > SHA_DIGEST_LENGTH)
479 entropy = SHA_DIGEST_LENGTH;
480
481 /* Stir it in */
482 RAND_add(hash, sizeof(hash), entropy);
483
484 debug3("Got %0.2f bytes of entropy from '%s'",
485 entropy, entropy_cmds[c].cmdstring);
486
487 total_entropy += entropy;
488
489 /* Execution time should be a bit unpredictable */
490 total_entropy += stir_gettimeofday(0.05);
491 total_entropy += stir_clock(0.05);
492 total_entropy += stir_rusage(RUSAGE_SELF, 0.1);
493 total_entropy += stir_rusage(RUSAGE_CHILDREN, 0.1);
494 } else {
495 debug2("Command '%s' disabled (badness %d)",
496 entropy_cmds[c].cmdstring,
497 entropy_cmds[c].badness);
498
499 if (entropy_cmds[c].badness > 0)
500 entropy_cmds[c].badness--;
501 }
502 }
503
504 return total_entropy;
526} 505}
527 506
528/* 507/*
529 * prng seedfile functions 508 * prng seedfile functions
530 */ 509 */
531int 510int
532prng_check_seedfile(char *filename) { 511prng_check_seedfile(char *filename)
533 512{
534 struct stat st; 513 struct stat st;
535 514
536 /* FIXME raceable: eg replace seed between this stat and subsequent open */ 515 /*
537 /* Not such a problem because we don't trust the seed file anyway */ 516 * XXX raceable: eg replace seed between this stat and subsequent
517 * open. Not such a problem because we don't really trust the
518 * seed file anyway.
519 * XXX: use secure path checking as elsewhere in OpenSSH
520 */
538 if (lstat(filename, &st) == -1) { 521 if (lstat(filename, &st) == -1) {
539 /* Give up on hard errors */ 522 /* Give up on hard errors */
540 if (errno != ENOENT) 523 if (errno != ENOENT)
541 debug("WARNING: Couldn't stat random seed file \"%s\": %s", 524 debug("WARNING: Couldn't stat random seed file "
542 filename, strerror(errno)); 525 "\"%.100s\": %s", filename, strerror(errno));
543 526 return 0;
544 return(0);
545 } 527 }
546 528
547 /* regular file? */ 529 /* regular file? */
548 if (!S_ISREG(st.st_mode)) 530 if (!S_ISREG(st.st_mode))
549 fatal("PRNG seedfile %.100s is not a regular file", filename); 531 fatal("PRNG seedfile %.100s is not a regular file",
532 filename);
550 533
551 /* mode 0600, owned by root or the current user? */ 534 /* mode 0600, owned by root or the current user? */
552 if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) { 535 if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) {
553 debug("WARNING: PRNG seedfile %.100s must be mode 0600, owned by uid %d", 536 debug("WARNING: PRNG seedfile %.100s must be mode 0600, "
554 filename, getuid()); 537 "owned by uid %d", filename, getuid());
555 return(0); 538 return 0;
556 } 539 }
557 540
558 return(1); 541 return 1;
559} 542}
560 543
561void 544void
562prng_write_seedfile(void) { 545prng_write_seedfile(void)
546{
563 int fd; 547 int fd;
564 char seed[1024]; 548 char seed[SEED_FILE_SIZE], filename[MAXPATHLEN];
565 char filename[1024];
566 struct passwd *pw; 549 struct passwd *pw;
567 550
568 pw = getpwuid(getuid()); 551 pw = getpwuid(getuid());
569 if (pw == NULL) 552 if (pw == NULL)
570 fatal("Couldn't get password entry for current user (%i): %s", 553 fatal("Couldn't get password entry for current user "
571 getuid(), strerror(errno)); 554 "(%i): %s", getuid(), strerror(errno));
572 555
573 /* Try to ensure that the parent directory is there */ 556 /* Try to ensure that the parent directory is there */
574 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, 557 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
575 _PATH_SSH_USER_DIR); 558 _PATH_SSH_USER_DIR);
576 mkdir(filename, 0700); 559 mkdir(filename, 0700);
577 560
578 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, 561 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
579 SSH_PRNG_SEED_FILE); 562 SSH_PRNG_SEED_FILE);
580 563
581 debug("writing PRNG seed to file %.100s", filename); 564 debug("writing PRNG seed to file %.100s", filename);
582 565
@@ -586,27 +569,27 @@ prng_write_seedfile(void) {
586 prng_check_seedfile(filename); 569 prng_check_seedfile(filename);
587 570
588 if ((fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600)) == -1) { 571 if ((fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600)) == -1) {
589 debug("WARNING: couldn't access PRNG seedfile %.100s (%.100s)", 572 debug("WARNING: couldn't access PRNG seedfile %.100s "
590 filename, strerror(errno)); 573 "(%.100s)", filename, strerror(errno));
591 } else { 574 } else {
592 if (atomicio(write, fd, &seed, sizeof(seed)) != sizeof(seed)) 575 if (atomicio(write, fd, &seed, sizeof(seed)) < sizeof(seed))
593 fatal("problem writing PRNG seedfile %.100s (%.100s)", filename, 576 fatal("problem writing PRNG seedfile %.100s "
594 strerror(errno)); 577 "(%.100s)", filename, strerror(errno));
595 close(fd); 578 close(fd);
596 } 579 }
597} 580}
598 581
599void 582void
600prng_read_seedfile(void) { 583prng_read_seedfile(void)
584{
601 int fd; 585 int fd;
602 char seed[1024]; 586 char seed[SEED_FILE_SIZE], filename[MAXPATHLEN];
603 char filename[1024];
604 struct passwd *pw; 587 struct passwd *pw;
605 588
606 pw = getpwuid(getuid()); 589 pw = getpwuid(getuid());
607 if (pw == NULL) 590 if (pw == NULL)
608 fatal("Couldn't get password entry for current user (%i): %s", 591 fatal("Couldn't get password entry for current user "
609 getuid(), strerror(errno)); 592 "(%i): %s", getuid(), strerror(errno));
610 593
611 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, 594 snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
612 SSH_PRNG_SEED_FILE); 595 SSH_PRNG_SEED_FILE);
@@ -614,19 +597,19 @@ prng_read_seedfile(void) {
614 debug("loading PRNG seed from file %.100s", filename); 597 debug("loading PRNG seed from file %.100s", filename);
615 598
616 if (!prng_check_seedfile(filename)) { 599 if (!prng_check_seedfile(filename)) {
617 verbose("Random seed file not found or not valid, ignoring."); 600 verbose("Random seed file not found or invalid, ignoring.");
618 return; 601 return;
619 } 602 }
620 603
621 /* open the file and read in the seed */ 604 /* open the file and read in the seed */
622 fd = open(filename, O_RDONLY); 605 fd = open(filename, O_RDONLY);
623 if (fd == -1) 606 if (fd == -1)
624 fatal("could not open PRNG seedfile %.100s (%.100s)", filename, 607 fatal("could not open PRNG seedfile %.100s (%.100s)",
625 strerror(errno)); 608 filename, strerror(errno));
626 609
627 if (atomicio(read, fd, &seed, sizeof(seed)) != sizeof(seed)) { 610 if (atomicio(read, fd, &seed, sizeof(seed)) < sizeof(seed)) {
628 verbose("invalid or short read from PRNG seedfile %.100s - ignoring", 611 verbose("invalid or short read from PRNG seedfile "
629 filename); 612 "%.100s - ignoring", filename);
630 memset(seed, '\0', sizeof(seed)); 613 memset(seed, '\0', sizeof(seed));
631 } 614 }
632 close(fd); 615 close(fd);
@@ -642,81 +625,78 @@ prng_read_seedfile(void) {
642int 625int
643prng_read_commands(char *cmdfilename) 626prng_read_commands(char *cmdfilename)
644{ 627{
645 FILE *f; 628 char cmd[SEED_FILE_SIZE], *cp, line[1024], path[SEED_FILE_SIZE];
646 char *cp;
647 char line[1024];
648 char cmd[1024];
649 char path[256];
650 int linenum;
651 int num_cmds = 64;
652 int cur_cmd = 0;
653 double est; 629 double est;
654 entropy_source_t *entcmd; 630 entropy_cmd_t *entcmd;
631 FILE *f;
632 int cur_cmd, linenum, num_cmds, arg;
655 633
656 f = fopen(cmdfilename, "r"); 634 if ((f = fopen(cmdfilename, "r")) == NULL) {
657 if (!f) {
658 fatal("couldn't read entropy commands file %.100s: %.100s", 635 fatal("couldn't read entropy commands file %.100s: %.100s",
659 cmdfilename, strerror(errno)); 636 cmdfilename, strerror(errno));
660 } 637 }
661 638
662 entcmd = (entropy_source_t *)xmalloc(num_cmds * sizeof(entropy_source_t)); 639 num_cmds = 64;
663 memset(entcmd, '\0', num_cmds * sizeof(entropy_source_t)); 640 entcmd = xmalloc(num_cmds * sizeof(entropy_cmd_t));
641 memset(entcmd, '\0', num_cmds * sizeof(entropy_cmd_t));
664 642
665 /* Read in file */ 643 /* Read in file */
666 linenum = 0; 644 cur_cmd = linenum = 0;
667 while (fgets(line, sizeof(line), f)) { 645 while (fgets(line, sizeof(line), f)) {
668 int arg;
669 char *argv;
670
671 linenum++; 646 linenum++;
672 647
673 /* skip leading whitespace, test for blank line or comment */ 648 /* Skip leading whitespace, blank lines and comments */
674 cp = line + strspn(line, WHITESPACE); 649 cp = line + strspn(line, WHITESPACE);
675 if ((*cp == 0) || (*cp == '#')) 650 if ((*cp == 0) || (*cp == '#'))
676 continue; /* done with this line */ 651 continue; /* done with this line */
677 652
678 /* First non-whitespace char should be double quote delimiting */ 653 /*
679 /* commandline */ 654 * The first non-whitespace char should be a double quote
655 * delimiting the commandline
656 */
680 if (*cp != '"') { 657 if (*cp != '"') {
681 error("bad entropy command, %.100s line %d", cmdfilename, 658 error("bad entropy command, %.100s line %d",
682 linenum); 659 cmdfilename, linenum);
683 continue; 660 continue;
684 } 661 }
685 662
686 /* first token, command args (incl. argv[0]) in double quotes */ 663 /*
664 * First token, command args (incl. argv[0]) in double
665 * quotes
666 */
687 cp = strtok(cp, "\""); 667 cp = strtok(cp, "\"");
688 if (cp == NULL) { 668 if (cp == NULL) {
689 error("missing or bad command string, %.100s line %d -- ignored", 669 error("missing or bad command string, %.100s "
690 cmdfilename, linenum); 670 "line %d -- ignored", cmdfilename, linenum);
691 continue; 671 continue;
692 } 672 }
693 strlcpy(cmd, cp, sizeof(cmd)); 673 strlcpy(cmd, cp, sizeof(cmd));
694 674
695 /* second token, full command path */ 675 /* Second token, full command path */
696 if ((cp = strtok(NULL, WHITESPACE)) == NULL) { 676 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
697 error("missing command path, %.100s line %d -- ignored", 677 error("missing command path, %.100s "
698 cmdfilename, linenum); 678 "line %d -- ignored", cmdfilename, linenum);
699 continue; 679 continue;
700 } 680 }
701 681
702 /* did configure mark this as dead? */ 682 /* Did configure mark this as dead? */
703 if (strncmp("undef", cp, 5) == 0) 683 if (strncmp("undef", cp, 5) == 0)
704 continue; 684 continue;
705 685
706 strlcpy(path, cp, sizeof(path)); 686 strlcpy(path, cp, sizeof(path));
707 687
708 /* third token, entropy rate estimate for this command */ 688 /* Third token, entropy rate estimate for this command */
709 if ((cp = strtok(NULL, WHITESPACE)) == NULL) { 689 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
710 error("missing entropy estimate, %.100s line %d -- ignored", 690 error("missing entropy estimate, %.100s "
711 cmdfilename, linenum); 691 "line %d -- ignored", cmdfilename, linenum);
712 continue; 692 continue;
713 } 693 }
714 est = strtod(cp, &argv); 694 est = strtod(cp, NULL);
715 695
716 /* end of line */ 696 /* end of line */
717 if ((cp = strtok(NULL, WHITESPACE)) != NULL) { 697 if ((cp = strtok(NULL, WHITESPACE)) != NULL) {
718 error("garbage at end of line %d in %.100s -- ignored", linenum, 698 error("garbage at end of line %d in %.100s "
719 cmdfilename); 699 "-- ignored", linenum, cmdfilename);
720 continue; 700 continue;
721 } 701 }
722 702
@@ -726,17 +706,14 @@ prng_read_commands(char *cmdfilename)
726 /* split the command args */ 706 /* split the command args */
727 cp = strtok(cmd, WHITESPACE); 707 cp = strtok(cmd, WHITESPACE);
728 arg = 0; 708 arg = 0;
729 argv = NULL;
730 do { 709 do {
731 char *s = (char*)xmalloc(strlen(cp) + 1); 710 entcmd[cur_cmd].args[arg] = xstrdup(cp);
732 strncpy(s, cp, strlen(cp) + 1);
733 entcmd[cur_cmd].args[arg] = s;
734 arg++; 711 arg++;
735 } while ((arg < 5) && (cp = strtok(NULL, WHITESPACE))); 712 } while(arg < NUM_ARGS && (cp = strtok(NULL, WHITESPACE)));
736 713
737 if (strtok(NULL, WHITESPACE)) 714 if (strtok(NULL, WHITESPACE))
738 error("ignored extra command elements (max 5), %.100s line %d", 715 error("ignored extra commands (max %d), %.100s "
739 cmdfilename, linenum); 716 "line %d", NUM_ARGS, cmdfilename, linenum);
740 717
741 /* Copy the command path and rate estimate */ 718 /* Copy the command path and rate estimate */
742 entcmd[cur_cmd].path = xstrdup(path); 719 entcmd[cur_cmd].path = xstrdup(path);
@@ -747,56 +724,80 @@ prng_read_commands(char *cmdfilename)
747 724
748 cur_cmd++; 725 cur_cmd++;
749 726
750 /* If we've filled the array, reallocate it twice the size */ 727 /*
751 /* Do this now because even if this we're on the last command, 728 * If we've filled the array, reallocate it twice the size
752 we need another slot to mark the last entry */ 729 * Do this now because even if this we're on the last
730 * command we need another slot to mark the last entry
731 */
753 if (cur_cmd == num_cmds) { 732 if (cur_cmd == num_cmds) {
754 num_cmds *= 2; 733 num_cmds *= 2;
755 entcmd = xrealloc(entcmd, num_cmds * sizeof(entropy_source_t)); 734 entcmd = xrealloc(entcmd, num_cmds *
735 sizeof(entropy_cmd_t));
756 } 736 }
757 } 737 }
758 738
759 /* zero the last entry */ 739 /* zero the last entry */
760 memset(&entcmd[cur_cmd], '\0', sizeof(entropy_source_t)); 740 memset(&entcmd[cur_cmd], '\0', sizeof(entropy_cmd_t));
761 741
762 /* trim to size */ 742 /* trim to size */
763 entropy_sources = xrealloc(entcmd, (cur_cmd+1) * sizeof(entropy_source_t)); 743 entropy_cmds = xrealloc(entcmd, (cur_cmd + 1) *
764 744 sizeof(entropy_cmd_t));
765 debug("Loaded %d entropy commands from %.100s", cur_cmd, cmdfilename);
766
767 return (cur_cmd >= MIN_ENTROPY_SOURCES);
768}
769
770static void
771seed_openssl_rng(void)
772{
773 /* Read in collection commands */
774 if (!prng_read_commands(SSH_PRNG_COMMAND_FILE))
775 fatal("PRNG initialisation failed -- exiting.");
776
777 prng_read_seedfile();
778 745
779 debug("Seeded RNG with %i bytes from programs", 746 debug("Loaded %d entropy commands from %.100s", cur_cmd,
780 (int)stir_from_programs()); 747 cmdfilename);
781 debug("Seeded RNG with %i bytes from system calls",
782 (int)stir_from_system());
783 748
784 prng_write_seedfile(); 749 return cur_cmd < MIN_ENTROPY_SOURCES ? -1 : 0;
785} 750}
786 751
787#endif /* USE_PRNGD */
788
789int 752int
790main(int argc, char **argv) 753main(int argc, char **argv)
791{ 754{
792 unsigned char buf[48]; 755 unsigned char buf[OUTPUT_SEED_SIZE];
793 int ret; 756 int ret;
794 757
795 __progname = get_progname(argv[0]); 758 __progname = get_progname(argv[0]);
796 /* XXX: need some debugging mode */ 759 /* XXX: need some debugging mode */
797 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); 760 log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
798 761
799 seed_openssl_rng(); 762#ifdef USE_SEED_FILES
763 prng_read_seedfile();
764#endif
765
766 /*
767 * Seed the RNG from wherever we can
768 */
769
770 /* Take whatever is on the stack, but don't credit it */
771 RAND_add(buf, sizeof(buf), 0);
772
773 debug("Seeded RNG with %i bytes from system calls",
774 (int)stir_from_system());
775
776#ifdef PRNGD_PORT
777 if (get_random_bytes_prngd(buf, sizeof(buf), PRNGD_PORT,
778 NULL) == -1)
779 fatal("Entropy collection failed");
780 RAND_add(buf, sizeof(buf), sizeof(buf));
781#elif PRNGD_SOCKET
782 if (get_random_bytes_prngd(buf, sizeof(buf), PRNGD_SOCKET,
783 NULL) == -1)
784 fatal("Entropy collection failed");
785 RAND_add(buf, sizeof(buf), sizeof(buf));
786#else
787 /* Read in collection commands */
788 if (prng_read_commands(SSH_PRNG_COMMAND_FILE) == -1)
789 fatal("PRNG initialisation failed -- exiting.");
790 debug("Seeded RNG with %i bytes from programs",
791 (int)stir_from_programs());
792#endif
793
794#ifdef USE_SEED_FILES
795 prng_write_seedfile();
796#endif
797
798 /*
799 * Write the seed to stdout
800 */
800 801
801 if (!RAND_status()) 802 if (!RAND_status())
802 fatal("Not enough entropy in RNG"); 803 fatal("Not enough entropy in RNG");