diff options
author | Damien Miller <djm@mindrot.org> | 2002-01-21 23:44:12 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2002-01-21 23:44:12 +1100 |
commit | 7b10ef48771bc3649b6e5ea0b021a2270a5d62f8 (patch) | |
tree | 98d9fb3fa5718fccbb88903a812beae3e989507d | |
parent | a234451a706f091e1f1f60203ade0dca3d7e99b6 (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-- | ChangeLog | 10 | ||||
-rw-r--r-- | ssh-rand-helper.c | 697 |
2 files changed, 358 insertions, 349 deletions
@@ -1,3 +1,11 @@ | |||
1 | 20020121 | ||
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 | |||
1 | 20020114 | 9 | 20020114 |
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 | 42 | RCSID("$Id: ssh-rand-helper.c,v 1.3 2002/01/21 12:44:12 djm Exp $"); |
43 | extern char *__progname; | ||
44 | #else | ||
45 | char *__progname; | ||
46 | #endif | ||
47 | 43 | ||
48 | RCSID("$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 | ||
68 | extern char *__progname; | ||
69 | #else | ||
70 | char *__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 | 92 | typedef 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; */ | ||
109 | static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC; | ||
110 | |||
111 | /* this is initialised from a file, by prng_read_commands() */ | ||
112 | static entropy_cmd_t *entropy_cmds = NULL; | ||
113 | |||
114 | /* Prototypes */ | ||
115 | double stir_from_system(void); | ||
116 | double stir_from_programs(void); | ||
117 | double stir_gettimeofday(double entropy_estimate); | ||
118 | double stir_clock(double entropy_estimate); | ||
119 | double stir_rusage(int who, double entropy_estimate); | ||
120 | double hash_command_output(entropy_cmd_t *src, char *hash); | ||
121 | int 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 | */ | ||
88 | int | 132 | int |
89 | get_random_bytes(unsigned char *buf, int len) | 133 | get_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; | ||
125 | reopen: | 171 | reopen: |
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; |
178 | done: | 216 | done: |
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 | |||
185 | static void | ||
186 | seed_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; */ | ||
207 | static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC; | ||
208 | |||
209 | typedef 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 | |||
225 | double stir_from_system(void); | ||
226 | double stir_from_programs(void); | ||
227 | double stir_gettimeofday(double entropy_estimate); | ||
228 | double stir_clock(double entropy_estimate); | ||
229 | double stir_rusage(int who, double entropy_estimate); | ||
230 | double hash_output_from_command(entropy_source_t *src, char *hash); | ||
231 | |||
232 | /* this is initialised from a file, by prng_read_commands() */ | ||
233 | entropy_source_t *entropy_sources = NULL; | ||
234 | |||
235 | double | ||
236 | stir_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 | |||
263 | double | ||
264 | stir_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 | ||
319 | double | 223 | double |
@@ -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 | ||
332 | double | 236 | double |
@@ -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 | |||
365 | static int | 268 | static int |
366 | _get_timeval_msec_difference(struct timeval *t1, struct timeval *t2) { | 269 | timeval_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 | ||
374 | double | 278 | double |
375 | hash_output_from_command(entropy_source_t *src, char *hash) | 279 | hash_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 | |||
432 | double | ||
433 | stir_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 | |||
460 | double | ||
461 | stir_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 | */ |
531 | int | 510 | int |
532 | prng_check_seedfile(char *filename) { | 511 | prng_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 | ||
561 | void | 544 | void |
562 | prng_write_seedfile(void) { | 545 | prng_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 | ||
599 | void | 582 | void |
600 | prng_read_seedfile(void) { | 583 | prng_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) { | |||
642 | int | 625 | int |
643 | prng_read_commands(char *cmdfilename) | 626 | prng_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 | |||
770 | static void | ||
771 | seed_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 | |||
789 | int | 752 | int |
790 | main(int argc, char **argv) | 753 | main(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"); |