diff options
Diffstat (limited to 'entropy.c')
-rw-r--r-- | entropy.c | 384 |
1 files changed, 304 insertions, 80 deletions
@@ -35,7 +35,7 @@ | |||
35 | #include <openssl/rand.h> | 35 | #include <openssl/rand.h> |
36 | #include <openssl/sha.h> | 36 | #include <openssl/sha.h> |
37 | 37 | ||
38 | RCSID("$Id: entropy.c,v 1.5 2000/04/16 02:31:50 damien Exp $"); | 38 | RCSID("$Id: entropy.c,v 1.6 2000/04/29 23:30:46 damien Exp $"); |
39 | 39 | ||
40 | #ifdef EGD_SOCKET | 40 | #ifdef EGD_SOCKET |
41 | #ifndef offsetof | 41 | #ifndef offsetof |
@@ -109,87 +109,100 @@ void get_random_bytes(unsigned char *buf, int len) | |||
109 | #if !defined(EGD_SOCKET) && !defined(RANDOM_POOL) | 109 | #if !defined(EGD_SOCKET) && !defined(RANDOM_POOL) |
110 | /* | 110 | /* |
111 | * FIXME: proper entropy estimations. All current values are guesses | 111 | * FIXME: proper entropy estimations. All current values are guesses |
112 | * FIXME: Need timeout for slow moving programs | 112 | * FIXME: (ATL) do estimates at compile time? |
113 | * FIXME: More entropy sources | 113 | * FIXME: More entropy sources |
114 | * FIXME: (ATL) bring in entropy sources from file | ||
115 | * FIXME: (ATL) add heuristic to increase the timeout if needed | ||
114 | */ | 116 | */ |
115 | 117 | ||
116 | double stir_from_system(void); | 118 | /* slow command timeouts (all in milliseconds) */ |
117 | double stir_from_programs(void); | 119 | /* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */ |
118 | double stir_gettimeofday(double entropy_estimate); | 120 | static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC; |
119 | double stir_clock(double entropy_estimate); | 121 | |
120 | double stir_rusage(int who, double entropy_estimate); | 122 | static int prng_seed_loaded = 0; |
121 | double hash_output_from_command(const char *path, const char **args, char *hash); | 123 | static int prng_seed_saved = 0; |
122 | 124 | ||
123 | typedef struct | 125 | typedef struct |
124 | { | 126 | { |
125 | /* Proportion of data that is entropy */ | 127 | /* Proportion of data that is entropy */ |
126 | double rate; | 128 | double rate; |
129 | /* Counter goes positive if this command times out */ | ||
130 | unsigned int badness; | ||
131 | /* Increases by factor of two each timeout */ | ||
132 | unsigned int sticky_badness; | ||
127 | /* Path to executable */ | 133 | /* Path to executable */ |
128 | const char *path; | 134 | const char *path; |
129 | /* argv to pass to executable */ | 135 | /* argv to pass to executable */ |
130 | const char *args[5]; | 136 | const char *args[5]; |
131 | } entropy_source_t; | 137 | } entropy_source_t; |
132 | 138 | ||
139 | double stir_from_system(void); | ||
140 | double stir_from_programs(void); | ||
141 | double stir_gettimeofday(double entropy_estimate); | ||
142 | double stir_clock(double entropy_estimate); | ||
143 | double stir_rusage(int who, double entropy_estimate); | ||
144 | double hash_output_from_command(entropy_source_t *src, char *hash); | ||
145 | |||
133 | entropy_source_t entropy_sources[] = { | 146 | entropy_source_t entropy_sources[] = { |
134 | #ifdef PROG_LS | 147 | #ifdef PROG_LS |
135 | { 0.002, PROG_LS, { "ls", "-alni", "/var/log", NULL } }, | 148 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/log", NULL } }, |
136 | { 0.002, PROG_LS, { "ls", "-alni", "/var/adm", NULL } }, | 149 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/adm", NULL } }, |
137 | { 0.002, PROG_LS, { "ls", "-alni", "/var/mail", NULL } }, | 150 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/mail", NULL } }, |
138 | { 0.002, PROG_LS, { "ls", "-alni", "/var/spool/mail", NULL } }, | 151 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/var/spool/mail", NULL } }, |
139 | { 0.002, PROG_LS, { "ls", "-alni", "/proc", NULL } }, | 152 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/proc", NULL } }, |
140 | { 0.002, PROG_LS, { "ls", "-alni", "/tmp", NULL } }, | 153 | { 0.002, 0, 1, PROG_LS, { "ls", "-alni", "/tmp", NULL } }, |
141 | #endif | 154 | #endif |
142 | #ifdef PROG_NETSTAT | 155 | #ifdef PROG_NETSTAT |
143 | { 0.005, PROG_NETSTAT, { "netstat","-an", NULL, NULL } }, | 156 | { 0.005, 0, 1, PROG_NETSTAT, { "netstat","-an", NULL, NULL } }, |
144 | { 0.010, PROG_NETSTAT, { "netstat","-in", NULL, NULL } }, | 157 | { 0.010, 0, 1, PROG_NETSTAT, { "netstat","-in", NULL, NULL } }, |
145 | { 0.002, PROG_NETSTAT, { "netstat","-rn", NULL, NULL } }, | 158 | { 0.002, 0, 1, PROG_NETSTAT, { "netstat","-rn", NULL, NULL } }, |
146 | { 0.002, PROG_NETSTAT, { "netstat","-s", NULL, NULL } }, | 159 | { 0.002, 0, 1, PROG_NETSTAT, { "netstat","-s", NULL, NULL } }, |
147 | #endif | 160 | #endif |
148 | #ifdef PROG_ARP | 161 | #ifdef PROG_ARP |
149 | { 0.002, PROG_ARP, { "arp","-a","-n", NULL } }, | 162 | { 0.002, 0, 1, PROG_ARP, { "arp","-a","-n", NULL } }, |
150 | #endif | 163 | #endif |
151 | #ifdef PROG_IFCONFIG | 164 | #ifdef PROG_IFCONFIG |
152 | { 0.002, PROG_IFCONFIG, { "ifconfig", "-a", NULL, NULL } }, | 165 | { 0.002, 0, 1, PROG_IFCONFIG, { "ifconfig", "-a", NULL, NULL } }, |
153 | #endif | 166 | #endif |
154 | #ifdef PROG_PS | 167 | #ifdef PROG_PS |
155 | { 0.003, PROG_PS, { "ps", "laxww", NULL, NULL } }, | 168 | { 0.003, 0, 1, PROG_PS, { "ps", "laxww", NULL, NULL } }, |
156 | { 0.003, PROG_PS, { "ps", "-al", NULL, NULL } }, | 169 | { 0.003, 0, 1, PROG_PS, { "ps", "-al", NULL, NULL } }, |
157 | { 0.003, PROG_PS, { "ps", "-efl", NULL, NULL } }, | 170 | { 0.003, 0, 1, PROG_PS, { "ps", "-efl", NULL, NULL } }, |
158 | #endif | 171 | #endif |
159 | #ifdef PROG_W | 172 | #ifdef PROG_W |
160 | { 0.005, PROG_W, { "w", NULL, NULL, NULL } }, | 173 | { 0.005, 0, 1, PROG_W, { "w", NULL, NULL, NULL } }, |
161 | #endif | 174 | #endif |
162 | #ifdef PROG_WHO | 175 | #ifdef PROG_WHO |
163 | { 0.001, PROG_WHO, { "who","-i", NULL, NULL } }, | 176 | { 0.001, 0, 1, PROG_WHO, { "who","-i", NULL, NULL } }, |
164 | #endif | 177 | #endif |
165 | #ifdef PROG_LAST | 178 | #ifdef PROG_LAST |
166 | { 0.001, PROG_LAST, { "last", NULL, NULL, NULL } }, | 179 | { 0.001, 0, 1, PROG_LAST, { "last", NULL, NULL, NULL } }, |
167 | #endif | 180 | #endif |
168 | #ifdef PROG_LASTLOG | 181 | #ifdef PROG_LASTLOG |
169 | { 0.001, PROG_LASTLOG, { "lastlog", NULL, NULL, NULL } }, | 182 | { 0.001, 0, 1, PROG_LASTLOG, { "lastlog", NULL, NULL, NULL } }, |
170 | #endif | 183 | #endif |
171 | #ifdef PROG_DF | 184 | #ifdef PROG_DF |
172 | { 0.010, PROG_DF, { "df", NULL, NULL, NULL } }, | 185 | { 0.010, 0, 1, PROG_DF, { "df", NULL, NULL, NULL } }, |
173 | { 0.010, PROG_DF, { "df", "-i", NULL, NULL } }, | 186 | { 0.010, 0, 1, PROG_DF, { "df", "-i", NULL, NULL } }, |
174 | #endif | 187 | #endif |
175 | #ifdef PROG_VMSTAT | 188 | #ifdef PROG_VMSTAT |
176 | { 0.010, PROG_VMSTAT, { "vmstat", NULL, NULL, NULL } }, | 189 | { 0.010, 0, 1, PROG_VMSTAT, { "vmstat", NULL, NULL, NULL } }, |
177 | #endif | 190 | #endif |
178 | #ifdef PROG_UPTIME | 191 | #ifdef PROG_UPTIME |
179 | { 0.001, PROG_UPTIME, { "uptime", NULL, NULL, NULL } }, | 192 | { 0.001, 0, 1, PROG_UPTIME, { "uptime", NULL, NULL, NULL } }, |
180 | #endif | 193 | #endif |
181 | #ifdef PROG_IPCS | 194 | #ifdef PROG_IPCS |
182 | { 0.001, PROG_IPCS, { "-a", NULL, NULL, NULL } }, | 195 | { 0.001, 0, 1, PROG_IPCS, { "-a", NULL, NULL, NULL } }, |
183 | #endif | 196 | #endif |
184 | #ifdef PROG_TAIL | 197 | #ifdef PROG_TAIL |
185 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/log/messages", NULL, NULL } }, | 198 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/messages", NULL, NULL } }, |
186 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/log/syslog", NULL, NULL } }, | 199 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/syslog", NULL, NULL } }, |
187 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/adm/messages", NULL, NULL } }, | 200 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/messages", NULL, NULL } }, |
188 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/adm/syslog", NULL, NULL } }, | 201 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/syslog", NULL, NULL } }, |
189 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/log/maillog", NULL, NULL } }, | 202 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/log/maillog", NULL, NULL } }, |
190 | { 0.001, PROG_TAIL, { "tail", "-200", "/var/adm/maillog", NULL, NULL } }, | 203 | { 0.001, 0, 1, PROG_TAIL, { "tail", "-200", "/var/adm/maillog", NULL, NULL } }, |
191 | #endif | 204 | #endif |
192 | { 0.000, NULL, { NULL, NULL, NULL, NULL, NULL } }, | 205 | { 0.000, 0, 0, NULL, { NULL, NULL, NULL, NULL, NULL } }, |
193 | }; | 206 | }; |
194 | 207 | ||
195 | double | 208 | double |
@@ -236,37 +249,49 @@ stir_from_programs(void) | |||
236 | for(i = 0; i < 2; i++) { | 249 | for(i = 0; i < 2; i++) { |
237 | c = 0; | 250 | c = 0; |
238 | while (entropy_sources[c].path != NULL) { | 251 | while (entropy_sources[c].path != NULL) { |
239 | /* Hash output from command */ | ||
240 | entropy_estimate = hash_output_from_command(entropy_sources[c].path, | ||
241 | entropy_sources[c].args, hash); | ||
242 | 252 | ||
243 | /* Scale back entropy estimate according to command's rate */ | 253 | if (!entropy_sources[c].badness) { |
244 | entropy_estimate *= entropy_sources[c].rate; | 254 | /* Hash output from command */ |
255 | entropy_estimate = hash_output_from_command(&entropy_sources[c], hash); | ||
256 | |||
257 | /* Scale back entropy estimate according to command's rate */ | ||
258 | entropy_estimate *= entropy_sources[c].rate; | ||
245 | 259 | ||
246 | /* Upper bound of entropy estimate is SHA_DIGEST_LENGTH */ | 260 | /* Upper bound of entropy estimate is SHA_DIGEST_LENGTH */ |
247 | if (entropy_estimate > SHA_DIGEST_LENGTH) | 261 | if (entropy_estimate > SHA_DIGEST_LENGTH) |
248 | entropy_estimate = SHA_DIGEST_LENGTH; | 262 | entropy_estimate = SHA_DIGEST_LENGTH; |
249 | 263 | ||
250 | /* * Scale back estimates for subsequent passes through list */ | 264 | /* * Scale back estimates for subsequent passes through list */ |
251 | entropy_estimate /= 10.0 * (i + 1.0); | 265 | entropy_estimate /= 10.0 * (i + 1.0); |
252 | 266 | ||
253 | /* Stir it in */ | 267 | /* Stir it in */ |
254 | RAND_add(hash, sizeof(hash), entropy_estimate); | 268 | RAND_add(hash, sizeof(hash), entropy_estimate); |
255 | 269 | ||
256 | /* FIXME: turn this off later */ | 270 | /* FIXME: turn this off later */ |
257 | #if 1 | 271 | #if 1 |
258 | debug("Got %0.2f bytes of entropy from %s", entropy_estimate, | 272 | debug("Got %0.2f bytes of entropy from %s", entropy_estimate, |
259 | entropy_sources[c].path); | 273 | entropy_sources[c].path); |
260 | #endif | 274 | #endif |
261 | 275 | ||
262 | total_entropy_estimate += entropy_estimate; | 276 | total_entropy_estimate += entropy_estimate; |
263 | 277 | ||
264 | /* Execution times should be a little unpredictable */ | 278 | /* Execution times should be a little unpredictable */ |
265 | total_entropy_estimate += stir_gettimeofday(0.05); | 279 | total_entropy_estimate += stir_gettimeofday(0.05); |
266 | total_entropy_estimate += stir_clock(0.05); | 280 | total_entropy_estimate += stir_clock(0.05); |
267 | total_entropy_estimate += stir_rusage(RUSAGE_SELF, 0.1); | 281 | total_entropy_estimate += stir_rusage(RUSAGE_SELF, 0.1); |
268 | total_entropy_estimate += stir_rusage(RUSAGE_CHILDREN, 0.1); | 282 | total_entropy_estimate += stir_rusage(RUSAGE_CHILDREN, 0.1); |
269 | 283 | } else { | |
284 | /* FIXME: turn this off later */ | ||
285 | #if 1 | ||
286 | debug("Command '%s %s %s' disabled (badness %d)", | ||
287 | entropy_sources[c].path, entropy_sources[c].args[1], | ||
288 | entropy_sources[c].args[2], entropy_sources[c].badness); | ||
289 | #endif | ||
290 | |||
291 | if (entropy_sources[c].badness > 0) | ||
292 | entropy_sources[c].badness--; | ||
293 | } | ||
294 | |||
270 | c++; | 295 | c++; |
271 | } | 296 | } |
272 | } | 297 | } |
@@ -308,7 +333,7 @@ stir_rusage(int who, double entropy_estimate) | |||
308 | #ifdef HAVE_GETRUSAGE | 333 | #ifdef HAVE_GETRUSAGE |
309 | struct rusage ru; | 334 | struct rusage ru; |
310 | 335 | ||
311 | if (getrusage(who, &ru) == -1) | 336 | if (getrusage(who, &ru) == -1) |
312 | fatal("Couldn't getrusage: %s", strerror(errno)); | 337 | fatal("Couldn't getrusage: %s", strerror(errno)); |
313 | 338 | ||
314 | RAND_add(&ru, sizeof(ru), 0.1); | 339 | RAND_add(&ru, sizeof(ru), 0.1); |
@@ -320,10 +345,12 @@ stir_rusage(int who, double entropy_estimate) | |||
320 | } | 345 | } |
321 | 346 | ||
322 | double | 347 | double |
323 | hash_output_from_command(const char *path, const char **args, char *hash) | 348 | hash_output_from_command(entropy_source_t *src, char *hash) |
324 | { | 349 | { |
325 | static int devnull = -1; | 350 | static int devnull = -1; |
326 | int p[2]; | 351 | int p[2]; |
352 | fd_set rdset; | ||
353 | int cmd_eof = 0, error_abort = 0; | ||
327 | pid_t pid; | 354 | pid_t pid; |
328 | int status; | 355 | int status; |
329 | char buf[2048]; | 356 | char buf[2048]; |
@@ -347,18 +374,17 @@ hash_output_from_command(const char *path, const char **args, char *hash) | |||
347 | fatal("Couldn't fork: %s", strerror(errno)); | 374 | fatal("Couldn't fork: %s", strerror(errno)); |
348 | /* NOTREACHED */ | 375 | /* NOTREACHED */ |
349 | case 0: /* Child */ | 376 | case 0: /* Child */ |
350 | close(0); | 377 | dup2(devnull, STDIN_FILENO); |
351 | close(1); | 378 | dup2(p[1], STDOUT_FILENO); |
352 | close(2); | 379 | dup2(p[1], STDERR_FILENO); |
353 | dup2(devnull, 0); | ||
354 | dup2(p[1], 1); | ||
355 | dup2(p[1], 2); | ||
356 | close(p[0]); | 380 | close(p[0]); |
357 | close(p[1]); | 381 | close(p[1]); |
358 | close(devnull); | 382 | close(devnull); |
359 | 383 | ||
360 | execv(path, (char**)args); | 384 | execv(src->path, (char**)(src->args)); |
361 | debug("(child) Couldn't exec '%s': %s", path, strerror(errno)); | 385 | debug("(child) Couldn't exec '%s %s %s': %s", src->path, |
386 | src->args[1], src->args[2], strerror(errno)); | ||
387 | src->badness = src->sticky_badness = 128; | ||
362 | _exit(-1); | 388 | _exit(-1); |
363 | default: /* Parent */ | 389 | default: /* Parent */ |
364 | break; | 390 | break; |
@@ -371,31 +397,208 @@ hash_output_from_command(const char *path, const char **args, char *hash) | |||
371 | /* Hash output from child */ | 397 | /* Hash output from child */ |
372 | SHA1_Init(&sha); | 398 | SHA1_Init(&sha); |
373 | total_bytes_read = 0; | 399 | total_bytes_read = 0; |
374 | while ((bytes_read = read(p[0], buf, sizeof(buf))) > 0) { | 400 | |
375 | SHA1_Update(&sha, buf, bytes_read); | 401 | while (!error_abort && !cmd_eof) { |
376 | total_bytes_read += bytes_read; | 402 | int ret; |
377 | RAND_add(&bytes_read, sizeof(&bytes_read), 0.0); | 403 | struct timeval tv; |
378 | } | 404 | |
405 | FD_ZERO(&rdset); | ||
406 | FD_SET(p[0], &rdset); | ||
407 | tv.tv_sec = entropy_timeout_current / 1000; | ||
408 | tv.tv_usec = (entropy_timeout_current % 1000) * 1000; | ||
409 | |||
410 | ret = select(p[0]+1, &rdset, NULL, NULL, &tv); | ||
411 | switch (ret) { | ||
412 | case 0: | ||
413 | /* timer expired */ | ||
414 | error_abort = 1; | ||
415 | break; | ||
416 | |||
417 | case 1: | ||
418 | /* command input */ | ||
419 | bytes_read = read(p[0], buf, sizeof(buf)); | ||
420 | if (bytes_read == -1) { | ||
421 | error_abort = 1; | ||
422 | break; | ||
423 | } | ||
424 | SHA1_Update(&sha, buf, bytes_read); | ||
425 | total_bytes_read += bytes_read; | ||
426 | RAND_add(&bytes_read, sizeof(&bytes_read), 0.0); | ||
427 | cmd_eof = bytes_read ? 0 : 1; | ||
428 | |||
429 | break; | ||
430 | |||
431 | case -1: | ||
432 | default: | ||
433 | error("Command '%s %s': select() failed: %s", src->path, src->args[1], | ||
434 | strerror(errno)); | ||
435 | error_abort = 1; | ||
436 | break; | ||
437 | } /* switch ret */ | ||
438 | |||
439 | RAND_add(&tv, sizeof(&tv), 0.0); | ||
440 | } /* while !error_abort && !cmd_eof */ | ||
441 | |||
379 | SHA1_Final(hash, &sha); | 442 | SHA1_Final(hash, &sha); |
380 | 443 | ||
381 | close(p[0]); | 444 | close(p[0]); |
382 | 445 | ||
383 | if (waitpid(pid, &status, 0) == -1) { | 446 | if (waitpid(pid, &status, 0) == -1) { |
384 | error("Couldn't wait for child '%s' completion: %s", path, | 447 | error("Couldn't wait for child '%s %s' completion: %s", src->path, |
385 | strerror(errno)); | 448 | src->args[1], strerror(errno)); |
386 | return(-1); | 449 | /* return(-1); */ /* FIXME: (ATL) this doesn't feel right */ |
450 | return(0.0); | ||
387 | } | 451 | } |
388 | 452 | ||
389 | RAND_add(&status, sizeof(&status), 0.0); | 453 | RAND_add(&status, sizeof(&status), 0.0); |
390 | 454 | ||
391 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) | 455 | if (error_abort) { |
392 | return(0.0); | 456 | /* closing p[0] on timeout causes the entropy command to |
393 | else | 457 | * SIGPIPE. Take whatever output we got, and mark this command |
458 | * as slow */ | ||
459 | debug("Command %s %s timed out", src->path, src->args[1]); | ||
460 | src->sticky_badness *= 2; | ||
461 | src->badness = src->sticky_badness; | ||
394 | return(total_bytes_read); | 462 | return(total_bytes_read); |
463 | } | ||
464 | |||
465 | if (WIFEXITED(status)) { | ||
466 | if (WEXITSTATUS(status)==0) { | ||
467 | return(total_bytes_read); | ||
468 | } else { | ||
469 | debug("Exit status was %d", WEXITSTATUS(status)); | ||
470 | src->badness = src->sticky_badness = 128; | ||
471 | return (0.0); | ||
472 | } | ||
473 | } else if (WIFSIGNALED(status)) { | ||
474 | debug("Returned on uncaught signal %d !", status); | ||
475 | src->badness = src->sticky_badness = 128; | ||
476 | return(0.0); | ||
477 | } else | ||
478 | return(0.0); | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * prng seedfile functions | ||
483 | */ | ||
484 | int | ||
485 | prng_check_seedfile(char *filename) { | ||
486 | |||
487 | struct stat st; | ||
488 | |||
489 | /* FIXME raceable: eg replace seed between this stat and subsequent open */ | ||
490 | /* Not such a problem because we don't trust the seed file anyway */ | ||
491 | if (lstat(filename, &st) == -1) { | ||
492 | /* Fail on hard errors */ | ||
493 | if (errno != ENOENT) | ||
494 | fatal("Couldn't stat random seed file \"%s\": %s", filename, | ||
495 | strerror(errno)); | ||
496 | |||
497 | return(0); | ||
498 | } | ||
499 | |||
500 | /* regular file? */ | ||
501 | if (!S_ISREG(st.st_mode)) | ||
502 | fatal("PRNG seedfile %.100s is not a regular file", filename); | ||
503 | |||
504 | /* mode 0600, owned by root or the current user? */ | ||
505 | if (((st.st_mode & 0177) != 0) || !(st.st_uid == geteuid())) | ||
506 | fatal("PRNG seedfile %.100s must be mode 0600, owned by uid %d", | ||
507 | filename, getuid()); | ||
508 | |||
509 | return(1); | ||
510 | } | ||
511 | |||
512 | void | ||
513 | prng_write_seedfile(void) { | ||
514 | int fd; | ||
515 | char seed[1024]; | ||
516 | char filename[1024]; | ||
517 | struct passwd *pw; | ||
518 | |||
519 | /* Don't bother if we have already saved a seed */ | ||
520 | if (prng_seed_saved) | ||
521 | return; | ||
522 | |||
523 | pw = getpwuid(getuid()); | ||
524 | if (pw == NULL) | ||
525 | fatal("Couldn't get password entry for current user (%i): %s", | ||
526 | getuid(), strerror(errno)); | ||
527 | |||
528 | /* Try to ensure that the parent directory is there */ | ||
529 | snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, | ||
530 | SSH_USER_DIR); | ||
531 | mkdir(filename, 0700); | ||
532 | |||
533 | snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, | ||
534 | SSH_PRNG_SEED_FILE); | ||
535 | |||
536 | debug("writing PRNG seed to file %.100s", filename); | ||
537 | |||
538 | RAND_bytes(seed, sizeof(seed)); | ||
539 | |||
540 | /* Don't care if the seed doesn't exist */ | ||
541 | prng_check_seedfile(filename); | ||
542 | |||
543 | if ((fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0600)) == -1) | ||
544 | fatal("couldn't access PRNG seedfile %.100s (%.100s)", filename, | ||
545 | strerror(errno)); | ||
546 | |||
547 | if (atomicio(write, fd, &seed, sizeof(seed)) != sizeof(seed)) | ||
548 | fatal("problem writing PRNG seedfile %.100s (%.100s)", filename, | ||
549 | strerror(errno)); | ||
550 | |||
551 | close(fd); | ||
552 | } | ||
553 | |||
554 | void | ||
555 | prng_read_seedfile(void) { | ||
556 | int fd; | ||
557 | char seed[1024]; | ||
558 | char filename[1024]; | ||
559 | struct passwd *pw; | ||
560 | |||
561 | pw = getpwuid(getuid()); | ||
562 | if (pw == NULL) | ||
563 | fatal("Couldn't get password entry for current user (%i): %s", | ||
564 | getuid(), strerror(errno)); | ||
565 | |||
566 | snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, | ||
567 | SSH_PRNG_SEED_FILE); | ||
568 | |||
569 | debug("loading PRNG seed from file %.100s", filename); | ||
570 | |||
571 | if (!prng_check_seedfile(filename)) { | ||
572 | verbose("Random seed file not found, creating new"); | ||
573 | prng_write_seedfile(); | ||
574 | |||
575 | /* Reseed immediatly */ | ||
576 | (void)stir_from_system(); | ||
577 | (void)stir_from_programs(); | ||
578 | return; | ||
579 | } | ||
580 | |||
581 | /* open the file and read in the seed */ | ||
582 | fd = open(filename, O_RDONLY); | ||
583 | if (fd == -1) | ||
584 | fatal("could not open PRNG seedfile %.100s (%.100s)", filename, | ||
585 | strerror(errno)); | ||
586 | |||
587 | if (atomicio(read, fd, &seed, sizeof(seed)) != sizeof(seed)) { | ||
588 | verbose("invalid or short read from PRNG seedfile %.100s - ignoring", | ||
589 | filename); | ||
590 | memset(seed, '\0', sizeof(seed)); | ||
591 | } | ||
592 | close(fd); | ||
593 | |||
594 | /* stir in the seed, with estimated entropy zero */ | ||
595 | RAND_add(&seed, sizeof(seed), 0.0); | ||
395 | } | 596 | } |
597 | |||
396 | #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ | 598 | #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ |
397 | 599 | ||
398 | #if defined(EGD_SOCKET) || defined(RANDOM_POOL) | 600 | #if defined(EGD_SOCKET) || defined(RANDOM_POOL) |
601 | |||
399 | /* | 602 | /* |
400 | * Seed OpenSSL's random number pool from Kernel random number generator | 603 | * Seed OpenSSL's random number pool from Kernel random number generator |
401 | * or EGD | 604 | * or EGD |
@@ -410,9 +613,21 @@ seed_rng(void) | |||
410 | RAND_add(buf, sizeof(buf), sizeof(buf)); | 613 | RAND_add(buf, sizeof(buf), sizeof(buf)); |
411 | memset(buf, '\0', sizeof(buf)); | 614 | memset(buf, '\0', sizeof(buf)); |
412 | } | 615 | } |
616 | |||
413 | #else /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ | 617 | #else /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ |
618 | |||
619 | /* | ||
620 | * Write a keyfile at exit | ||
621 | */ | ||
622 | void | ||
623 | prng_seed_cleanup(void *junk) | ||
624 | { | ||
625 | prng_write_seedfile(); | ||
626 | } | ||
627 | |||
414 | /* | 628 | /* |
415 | * Conditionally Seed OpenSSL's random number pool syscalls and program output | 629 | * Conditionally Seed OpenSSL's random number pool from |
630 | * syscalls and program output | ||
416 | */ | 631 | */ |
417 | void | 632 | void |
418 | seed_rng(void) | 633 | seed_rng(void) |
@@ -422,5 +637,14 @@ seed_rng(void) | |||
422 | debug("%i bytes from system calls", (int)stir_from_system()); | 637 | debug("%i bytes from system calls", (int)stir_from_system()); |
423 | debug("%i bytes from programs", (int)stir_from_programs()); | 638 | debug("%i bytes from programs", (int)stir_from_programs()); |
424 | debug("OpenSSL random status is now %i\n", RAND_status()); | 639 | debug("OpenSSL random status is now %i\n", RAND_status()); |
640 | |||
641 | if (!prng_seed_loaded) | ||
642 | { | ||
643 | prng_seed_loaded = 1; | ||
644 | prng_seed_saved = 0; | ||
645 | prng_read_seedfile(); | ||
646 | fatal_add_cleanup(prng_seed_cleanup, NULL); | ||
647 | atexit(prng_write_seedfile); | ||
648 | } | ||
425 | } | 649 | } |
426 | #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ | 650 | #endif /* defined(EGD_SOCKET) || defined(RANDOM_POOL) */ |