]> andersk Git - openssh.git/blob - ssh-rand-helper.c
- stevesk@cvs.openbsd.org 2006/07/12 22:42:32
[openssh.git] / ssh-rand-helper.c
1 /*
2  * Copyright (c) 2001-2002 Damien Miller.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "includes.h"
26
27 #include <sys/types.h>
28 #include <sys/resource.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <sys/socket.h>
32 #include <stddef.h>
33
34 #include <netinet/in.h>
35
36 #ifdef HAVE_SYS_UN_H
37 # include <sys/un.h>
38 #endif
39
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <pwd.h>
43 #include <signal.h>
44
45 #include <openssl/rand.h>
46 #include <openssl/sha.h>
47 #include <openssl/crypto.h>
48
49 /* SunOS 4.4.4 needs this */
50 #ifdef HAVE_FLOATINGPOINT_H
51 # include <floatingpoint.h>
52 #endif /* HAVE_FLOATINGPOINT_H */
53
54 #include "misc.h"
55 #include "xmalloc.h"
56 #include "atomicio.h"
57 #include "pathnames.h"
58 #include "log.h"
59
60 /* Number of bytes we write out */
61 #define OUTPUT_SEED_SIZE        48
62
63 /* Length of on-disk seedfiles */
64 #define SEED_FILE_SIZE          1024
65
66 /* Maximum number of command-line arguments to read from file */
67 #define NUM_ARGS                10
68
69 /* Minimum number of usable commands to be considered sufficient */
70 #define MIN_ENTROPY_SOURCES     16
71
72 /* Path to on-disk seed file (relative to user's home directory */
73 #ifndef SSH_PRNG_SEED_FILE
74 # define SSH_PRNG_SEED_FILE      _PATH_SSH_USER_DIR"/prng_seed"
75 #endif
76
77 /* Path to PRNG commands list */
78 #ifndef SSH_PRNG_COMMAND_FILE
79 # define SSH_PRNG_COMMAND_FILE   SSHDIR "/ssh_prng_cmds"
80 #endif
81
82 extern char *__progname;
83
84 #define WHITESPACE " \t\n"
85
86 #ifndef RUSAGE_SELF
87 # define RUSAGE_SELF 0
88 #endif
89 #ifndef RUSAGE_CHILDREN
90 # define RUSAGE_CHILDREN 0
91 #endif
92
93 #if !defined(PRNGD_SOCKET) && !defined(PRNGD_PORT)
94 # define USE_SEED_FILES
95 #endif
96
97 typedef struct {
98         /* Proportion of data that is entropy */
99         double rate;
100         /* Counter goes positive if this command times out */
101         unsigned int badness;
102         /* Increases by factor of two each timeout */
103         unsigned int sticky_badness;
104         /* Path to executable */
105         char *path;
106         /* argv to pass to executable */
107         char *args[NUM_ARGS]; /* XXX: arbitrary limit */
108         /* full command string (debug) */
109         char *cmdstring;
110 } entropy_cmd_t;
111
112 /* slow command timeouts (all in milliseconds) */
113 /* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */
114 static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;
115
116 /* this is initialised from a file, by prng_read_commands() */
117 static entropy_cmd_t *entropy_cmds = NULL;
118
119 /* Prototypes */
120 double stir_from_system(void);
121 double stir_from_programs(void);
122 double stir_gettimeofday(double entropy_estimate);
123 double stir_clock(double entropy_estimate);
124 double stir_rusage(int who, double entropy_estimate);
125 double hash_command_output(entropy_cmd_t *src, unsigned char *hash);
126 int get_random_bytes_prngd(unsigned char *buf, int len,
127     unsigned short tcp_port, char *socket_path);
128
129 /*
130  * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon
131  * listening either on 'tcp_port', or via Unix domain socket at *
132  * 'socket_path'.
133  * Either a non-zero tcp_port or a non-null socket_path must be
134  * supplied.
135  * Returns 0 on success, -1 on error
136  */
137 int
138 get_random_bytes_prngd(unsigned char *buf, int len,
139     unsigned short tcp_port, char *socket_path)
140 {
141         int fd, addr_len, rval, errors;
142         u_char msg[2];
143         struct sockaddr_storage addr;
144         struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr;
145         struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr;
146         mysig_t old_sigpipe;
147
148         /* Sanity checks */
149         if (socket_path == NULL && tcp_port == 0)
150                 fatal("You must specify a port or a socket");
151         if (socket_path != NULL &&
152             strlen(socket_path) >= sizeof(addr_un->sun_path))
153                 fatal("Random pool path is too long");
154         if (len <= 0 || len > 255)
155                 fatal("Too many bytes (%d) to read from PRNGD", len);
156
157         memset(&addr, '\0', sizeof(addr));
158
159         if (tcp_port != 0) {
160                 addr_in->sin_family = AF_INET;
161                 addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
162                 addr_in->sin_port = htons(tcp_port);
163                 addr_len = sizeof(*addr_in);
164         } else {
165                 addr_un->sun_family = AF_UNIX;
166                 strlcpy(addr_un->sun_path, socket_path,
167                     sizeof(addr_un->sun_path));
168                 addr_len = offsetof(struct sockaddr_un, sun_path) +
169                     strlen(socket_path) + 1;
170         }
171
172         old_sigpipe = mysignal(SIGPIPE, SIG_IGN);
173
174         errors = 0;
175         rval = -1;
176 reopen:
177         fd = socket(addr.ss_family, SOCK_STREAM, 0);
178         if (fd == -1) {
179                 error("Couldn't create socket: %s", strerror(errno));
180                 goto done;
181         }
182
183         if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) {
184                 if (tcp_port != 0) {
185                         error("Couldn't connect to PRNGD port %d: %s",
186                             tcp_port, strerror(errno));
187                 } else {
188                         error("Couldn't connect to PRNGD socket \"%s\": %s",
189                             addr_un->sun_path, strerror(errno));
190                 }
191                 goto done;
192         }
193
194         /* Send blocking read request to PRNGD */
195         msg[0] = 0x02;
196         msg[1] = len;
197
198         if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) {
199                 if (errno == EPIPE && errors < 10) {
200                         close(fd);
201                         errors++;
202                         goto reopen;
203                 }
204                 error("Couldn't write to PRNGD socket: %s",
205                     strerror(errno));
206                 goto done;
207         }
208
209         if (atomicio(read, fd, buf, len) != (size_t)len) {
210                 if (errno == EPIPE && errors < 10) {
211                         close(fd);
212                         errors++;
213                         goto reopen;
214                 }
215                 error("Couldn't read from PRNGD socket: %s",
216                     strerror(errno));
217                 goto done;
218         }
219
220         rval = 0;
221 done:
222         mysignal(SIGPIPE, old_sigpipe);
223         if (fd != -1)
224                 close(fd);
225         return rval;
226 }
227
228 static int
229 seed_from_prngd(unsigned char *buf, size_t bytes)
230 {
231 #ifdef PRNGD_PORT
232         debug("trying egd/prngd port %d", PRNGD_PORT);
233         if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == 0)
234                 return 0;
235 #endif
236 #ifdef PRNGD_SOCKET
237         debug("trying egd/prngd socket %s", PRNGD_SOCKET);
238         if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == 0)
239                 return 0;
240 #endif
241         return -1;
242 }
243
244 double
245 stir_gettimeofday(double entropy_estimate)
246 {
247         struct timeval tv;
248
249         if (gettimeofday(&tv, NULL) == -1)
250                 fatal("Couldn't gettimeofday: %s", strerror(errno));
251
252         RAND_add(&tv, sizeof(tv), entropy_estimate);
253
254         return entropy_estimate;
255 }
256
257 double
258 stir_clock(double entropy_estimate)
259 {
260 #ifdef HAVE_CLOCK
261         clock_t c;
262
263         c = clock();
264         RAND_add(&c, sizeof(c), entropy_estimate);
265
266         return entropy_estimate;
267 #else /* _HAVE_CLOCK */
268         return 0;
269 #endif /* _HAVE_CLOCK */
270 }
271
272 double
273 stir_rusage(int who, double entropy_estimate)
274 {
275 #ifdef HAVE_GETRUSAGE
276         struct rusage ru;
277
278         if (getrusage(who, &ru) == -1)
279                 return 0;
280
281         RAND_add(&ru, sizeof(ru), entropy_estimate);
282
283         return entropy_estimate;
284 #else /* _HAVE_GETRUSAGE */
285         return 0;
286 #endif /* _HAVE_GETRUSAGE */
287 }
288
289 static int
290 timeval_diff(struct timeval *t1, struct timeval *t2)
291 {
292         int secdiff, usecdiff;
293
294         secdiff = t2->tv_sec - t1->tv_sec;
295         usecdiff = (secdiff*1000000) + (t2->tv_usec - t1->tv_usec);
296         return (int)(usecdiff / 1000);
297 }
298
299 double
300 hash_command_output(entropy_cmd_t *src, unsigned char *hash)
301 {
302         char buf[8192];
303         fd_set rdset;
304         int bytes_read, cmd_eof, error_abort, msec_elapsed, p[2];
305         int status, total_bytes_read;
306         static int devnull = -1;
307         pid_t pid;
308         SHA_CTX sha;
309         struct timeval tv_start, tv_current;
310
311         debug3("Reading output from \'%s\'", src->cmdstring);
312
313         if (devnull == -1) {
314                 devnull = open("/dev/null", O_RDWR);
315                 if (devnull == -1)
316                         fatal("Couldn't open /dev/null: %s",
317                             strerror(errno));
318         }
319
320         if (pipe(p) == -1)
321                 fatal("Couldn't open pipe: %s", strerror(errno));
322
323         (void)gettimeofday(&tv_start, NULL); /* record start time */
324
325         switch (pid = fork()) {
326                 case -1: /* Error */
327                         close(p[0]);
328                         close(p[1]);
329                         fatal("Couldn't fork: %s", strerror(errno));
330                         /* NOTREACHED */
331                 case 0: /* Child */
332                         dup2(devnull, STDIN_FILENO);
333                         dup2(p[1], STDOUT_FILENO);
334                         dup2(p[1], STDERR_FILENO);
335                         close(p[0]);
336                         close(p[1]);
337                         close(devnull);
338
339                         execv(src->path, (char**)(src->args));
340
341                         debug("(child) Couldn't exec '%s': %s",
342                             src->cmdstring, strerror(errno));
343                         _exit(-1);
344                 default: /* Parent */
345                         break;
346         }
347
348         RAND_add(&pid, sizeof(&pid), 0.0);
349
350         close(p[1]);
351
352         /* Hash output from child */
353         SHA1_Init(&sha);
354
355         cmd_eof = error_abort = msec_elapsed = total_bytes_read = 0;
356         while (!error_abort && !cmd_eof) {
357                 int ret;
358                 struct timeval tv;
359                 int msec_remaining;
360
361                 (void) gettimeofday(&tv_current, 0);
362                 msec_elapsed = timeval_diff(&tv_start, &tv_current);
363                 if (msec_elapsed >= entropy_timeout_current) {
364                         error_abort=1;
365                         continue;
366                 }
367                 msec_remaining = entropy_timeout_current - msec_elapsed;
368
369                 FD_ZERO(&rdset);
370                 FD_SET(p[0], &rdset);
371                 tv.tv_sec = msec_remaining / 1000;
372                 tv.tv_usec = (msec_remaining % 1000) * 1000;
373
374                 ret = select(p[0] + 1, &rdset, NULL, NULL, &tv);
375
376                 RAND_add(&tv, sizeof(tv), 0.0);
377
378                 switch (ret) {
379                 case 0:
380                         /* timer expired */
381                         error_abort = 1;
382                         kill(pid, SIGINT);
383                         break;
384                 case 1:
385                         /* command input */
386                         do {
387                                 bytes_read = read(p[0], buf, sizeof(buf));
388                         } while (bytes_read == -1 && errno == EINTR);
389                         RAND_add(&bytes_read, sizeof(&bytes_read), 0.0);
390                         if (bytes_read == -1) {
391                                 error_abort = 1;
392                                 break;
393                         } else if (bytes_read) {
394                                 SHA1_Update(&sha, buf, bytes_read);
395                                 total_bytes_read += bytes_read;
396                         } else {
397                                 cmd_eof = 1;
398                         }
399                         break;
400                 case -1:
401                 default:
402                         /* error */
403                         debug("Command '%s': select() failed: %s",
404                             src->cmdstring, strerror(errno));
405                         error_abort = 1;
406                         break;
407                 }
408         }
409
410         SHA1_Final(hash, &sha);
411
412         close(p[0]);
413
414         debug3("Time elapsed: %d msec", msec_elapsed);
415
416         if (waitpid(pid, &status, 0) == -1) {
417                 error("Couldn't wait for child '%s' completion: %s",
418                     src->cmdstring, strerror(errno));
419                 return 0.0;
420         }
421
422         RAND_add(&status, sizeof(&status), 0.0);
423
424         if (error_abort) {
425                 /*
426                  * Closing p[0] on timeout causes the entropy command to
427                  * SIGPIPE. Take whatever output we got, and mark this
428                  * command as slow
429                  */
430                 debug2("Command '%s' timed out", src->cmdstring);
431                 src->sticky_badness *= 2;
432                 src->badness = src->sticky_badness;
433                 return total_bytes_read;
434         }
435
436         if (WIFEXITED(status)) {
437                 if (WEXITSTATUS(status) == 0) {
438                         return total_bytes_read;
439                 } else {
440                         debug2("Command '%s' exit status was %d",
441                             src->cmdstring, WEXITSTATUS(status));
442                         src->badness = src->sticky_badness = 128;
443                         return 0.0;
444                 }
445         } else if (WIFSIGNALED(status)) {
446                 debug2("Command '%s' returned on uncaught signal %d !",
447                     src->cmdstring, status);
448                 src->badness = src->sticky_badness = 128;
449                 return 0.0;
450         } else
451                 return 0.0;
452 }
453
454 double
455 stir_from_system(void)
456 {
457         double total_entropy_estimate;
458         long int i;
459
460         total_entropy_estimate = 0;
461
462         i = getpid();
463         RAND_add(&i, sizeof(i), 0.5);
464         total_entropy_estimate += 0.1;
465
466         i = getppid();
467         RAND_add(&i, sizeof(i), 0.5);
468         total_entropy_estimate += 0.1;
469
470         i = getuid();
471         RAND_add(&i, sizeof(i), 0.0);
472         i = getgid();
473         RAND_add(&i, sizeof(i), 0.0);
474
475         total_entropy_estimate += stir_gettimeofday(1.0);
476         total_entropy_estimate += stir_clock(0.5);
477         total_entropy_estimate += stir_rusage(RUSAGE_SELF, 2.0);
478
479         return total_entropy_estimate;
480 }
481
482 double
483 stir_from_programs(void)
484 {
485         int c;
486         double entropy, total_entropy;
487         unsigned char hash[SHA_DIGEST_LENGTH];
488
489         total_entropy = 0;
490         for(c = 0; entropy_cmds[c].path != NULL; c++) {
491                 if (!entropy_cmds[c].badness) {
492                         /* Hash output from command */
493                         entropy = hash_command_output(&entropy_cmds[c],
494                             hash);
495
496                         /* Scale back estimate by command's rate */
497                         entropy *= entropy_cmds[c].rate;
498
499                         /* Upper bound of entropy is SHA_DIGEST_LENGTH */
500                         if (entropy > SHA_DIGEST_LENGTH)
501                                 entropy = SHA_DIGEST_LENGTH;
502
503                         /* Stir it in */
504                         RAND_add(hash, sizeof(hash), entropy);
505
506                         debug3("Got %0.2f bytes of entropy from '%s'",
507                             entropy, entropy_cmds[c].cmdstring);
508
509                         total_entropy += entropy;
510
511                         /* Execution time should be a bit unpredictable */
512                         total_entropy += stir_gettimeofday(0.05);
513                         total_entropy += stir_clock(0.05);
514                         total_entropy += stir_rusage(RUSAGE_SELF, 0.1);
515                         total_entropy += stir_rusage(RUSAGE_CHILDREN, 0.1);
516                 } else {
517                         debug2("Command '%s' disabled (badness %d)",
518                             entropy_cmds[c].cmdstring,
519                             entropy_cmds[c].badness);
520
521                         if (entropy_cmds[c].badness > 0)
522                                 entropy_cmds[c].badness--;
523                 }
524         }
525
526         return total_entropy;
527 }
528
529 /*
530  * prng seedfile functions
531  */
532 int
533 prng_check_seedfile(char *filename)
534 {
535         struct stat st;
536
537         /*
538          * XXX raceable: eg replace seed between this stat and subsequent
539          * open. Not such a problem because we don't really trust the
540          * seed file anyway.
541          * XXX: use secure path checking as elsewhere in OpenSSH
542          */
543         if (lstat(filename, &st) == -1) {
544                 /* Give up on hard errors */
545                 if (errno != ENOENT)
546                         debug("WARNING: Couldn't stat random seed file "
547                             "\"%.100s\": %s", filename, strerror(errno));
548                 return 0;
549         }
550
551         /* regular file? */
552         if (!S_ISREG(st.st_mode))
553                 fatal("PRNG seedfile %.100s is not a regular file",
554                     filename);
555
556         /* mode 0600, owned by root or the current user? */
557         if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) {
558                 debug("WARNING: PRNG seedfile %.100s must be mode 0600, "
559                     "owned by uid %li", filename, (long int)getuid());
560                 return 0;
561         }
562
563         return 1;
564 }
565
566 void
567 prng_write_seedfile(void)
568 {
569         int fd, save_errno;
570         unsigned char seed[SEED_FILE_SIZE];
571         char filename[MAXPATHLEN], tmpseed[MAXPATHLEN];
572         struct passwd *pw;
573         mode_t old_umask;
574
575         pw = getpwuid(getuid());
576         if (pw == NULL)
577                 fatal("Couldn't get password entry for current user "
578                     "(%li): %s", (long int)getuid(), strerror(errno));
579
580         /* Try to ensure that the parent directory is there */
581         snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
582             _PATH_SSH_USER_DIR);
583         if (mkdir(filename, 0700) < 0 && errno != EEXIST)
584                 fatal("mkdir %.200s: %s", filename, strerror(errno));
585
586         snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
587             SSH_PRNG_SEED_FILE);
588
589         strlcpy(tmpseed, filename, sizeof(tmpseed));
590         if (strlcat(tmpseed, ".XXXXXXXXXX", sizeof(tmpseed)) >=
591             sizeof(tmpseed))
592                 fatal("PRNG seed filename too long");
593
594         if (RAND_bytes(seed, sizeof(seed)) <= 0)
595                 fatal("PRNG seed extraction failed");
596
597         /* Don't care if the seed doesn't exist */
598         prng_check_seedfile(filename);
599
600         old_umask = umask(0177);
601
602         if ((fd = mkstemp(tmpseed)) == -1) {
603                 debug("WARNING: couldn't make temporary PRNG seedfile %.100s "
604                     "(%.100s)", tmpseed, strerror(errno));
605         } else {
606                 debug("writing PRNG seed to file %.100s", tmpseed);
607                 if (atomicio(vwrite, fd, &seed, sizeof(seed)) < sizeof(seed)) {
608                         save_errno = errno;
609                         close(fd);
610                         unlink(tmpseed);
611                         fatal("problem writing PRNG seedfile %.100s "
612                             "(%.100s)", filename, strerror(save_errno));
613                 }
614                 close(fd);
615                 debug("moving temporary PRNG seed to file %.100s", filename);
616                 if (rename(tmpseed, filename) == -1) {
617                         save_errno = errno;
618                         unlink(tmpseed);
619                         fatal("problem renaming PRNG seedfile from %.100s "
620                             "to %.100s (%.100s)", tmpseed, filename,
621                             strerror(save_errno));
622                 }
623         }
624         umask(old_umask);
625 }
626
627 void
628 prng_read_seedfile(void)
629 {
630         int fd;
631         char seed[SEED_FILE_SIZE], filename[MAXPATHLEN];
632         struct passwd *pw;
633
634         pw = getpwuid(getuid());
635         if (pw == NULL)
636                 fatal("Couldn't get password entry for current user "
637                     "(%li): %s", (long int)getuid(), strerror(errno));
638
639         snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir,
640                 SSH_PRNG_SEED_FILE);
641
642         debug("loading PRNG seed from file %.100s", filename);
643
644         if (!prng_check_seedfile(filename)) {
645                 verbose("Random seed file not found or invalid, ignoring.");
646                 return;
647         }
648
649         /* open the file and read in the seed */
650         fd = open(filename, O_RDONLY);
651         if (fd == -1)
652                 fatal("could not open PRNG seedfile %.100s (%.100s)",
653                     filename, strerror(errno));
654
655         if (atomicio(read, fd, &seed, sizeof(seed)) < sizeof(seed)) {
656                 verbose("invalid or short read from PRNG seedfile "
657                     "%.100s - ignoring", filename);
658                 memset(seed, '\0', sizeof(seed));
659         }
660         close(fd);
661
662         /* stir in the seed, with estimated entropy zero */
663         RAND_add(&seed, sizeof(seed), 0.0);
664 }
665
666
667 /*
668  * entropy command initialisation functions
669  */
670 int
671 prng_read_commands(char *cmdfilename)
672 {
673         char cmd[SEED_FILE_SIZE], *cp, line[1024], path[SEED_FILE_SIZE];
674         double est;
675         entropy_cmd_t *entcmd;
676         FILE *f;
677         int cur_cmd, linenum, num_cmds, arg;
678
679         if ((f = fopen(cmdfilename, "r")) == NULL) {
680                 fatal("couldn't read entropy commands file %.100s: %.100s",
681                     cmdfilename, strerror(errno));
682         }
683
684         num_cmds = 64;
685         entcmd = xcalloc(num_cmds, sizeof(entropy_cmd_t));
686
687         /* Read in file */
688         cur_cmd = linenum = 0;
689         while (fgets(line, sizeof(line), f)) {
690                 linenum++;
691
692                 /* Skip leading whitespace, blank lines and comments */
693                 cp = line + strspn(line, WHITESPACE);
694                 if ((*cp == 0) || (*cp == '#'))
695                         continue; /* done with this line */
696
697                 /*
698                  * The first non-whitespace char should be a double quote
699                  * delimiting the commandline
700                  */
701                 if (*cp != '"') {
702                         error("bad entropy command, %.100s line %d",
703                             cmdfilename, linenum);
704                         continue;
705                 }
706
707                 /*
708                  * First token, command args (incl. argv[0]) in double
709                  * quotes
710                  */
711                 cp = strtok(cp, "\"");
712                 if (cp == NULL) {
713                         error("missing or bad command string, %.100s "
714                             "line %d -- ignored", cmdfilename, linenum);
715                         continue;
716                 }
717                 strlcpy(cmd, cp, sizeof(cmd));
718
719                 /* Second token, full command path */
720                 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
721                         error("missing command path, %.100s "
722                             "line %d -- ignored", cmdfilename, linenum);
723                         continue;
724                 }
725
726                 /* Did configure mark this as dead? */
727                 if (strncmp("undef", cp, 5) == 0)
728                         continue;
729
730                 strlcpy(path, cp, sizeof(path));
731
732                 /* Third token, entropy rate estimate for this command */
733                 if ((cp = strtok(NULL, WHITESPACE)) == NULL) {
734                         error("missing entropy estimate, %.100s "
735                             "line %d -- ignored", cmdfilename, linenum);
736                         continue;
737                 }
738                 est = strtod(cp, NULL);
739
740                 /* end of line */
741                 if ((cp = strtok(NULL, WHITESPACE)) != NULL) {
742                         error("garbage at end of line %d in %.100s "
743                             "-- ignored", linenum, cmdfilename);
744                         continue;
745                 }
746
747                 /* save the command for debug messages */
748                 entcmd[cur_cmd].cmdstring = xstrdup(cmd);
749
750                 /* split the command args */
751                 cp = strtok(cmd, WHITESPACE);
752                 arg = 0;
753                 do {
754                         entcmd[cur_cmd].args[arg] = xstrdup(cp);
755                         arg++;
756                 } while(arg < NUM_ARGS && (cp = strtok(NULL, WHITESPACE)));
757
758                 if (strtok(NULL, WHITESPACE))
759                         error("ignored extra commands (max %d), %.100s "
760                             "line %d", NUM_ARGS, cmdfilename, linenum);
761
762                 /* Copy the command path and rate estimate */
763                 entcmd[cur_cmd].path = xstrdup(path);
764                 entcmd[cur_cmd].rate = est;
765
766                 /* Initialise other values */
767                 entcmd[cur_cmd].sticky_badness = 1;
768
769                 cur_cmd++;
770
771                 /*
772                  * If we've filled the array, reallocate it twice the size
773                  * Do this now because even if this we're on the last
774                  * command we need another slot to mark the last entry
775                  */
776                 if (cur_cmd == num_cmds) {
777                         num_cmds *= 2;
778                         entcmd = xrealloc(entcmd, num_cmds,
779                             sizeof(entropy_cmd_t));
780                 }
781         }
782
783         /* zero the last entry */
784         memset(&entcmd[cur_cmd], '\0', sizeof(entropy_cmd_t));
785
786         /* trim to size */
787         entropy_cmds = xrealloc(entcmd, (cur_cmd + 1),
788             sizeof(entropy_cmd_t));
789
790         debug("Loaded %d entropy commands from %.100s", cur_cmd,
791             cmdfilename);
792
793         fclose(f);
794         return cur_cmd < MIN_ENTROPY_SOURCES ? -1 : 0;
795 }
796
797 void
798 usage(void)
799 {
800         fprintf(stderr, "Usage: %s [options]\n", __progname);
801         fprintf(stderr, "  -v          Verbose; display verbose debugging messages.\n");
802         fprintf(stderr, "              Multiple -v increases verbosity.\n");
803         fprintf(stderr, "  -x          Force output in hexadecimal (for debugging)\n");
804         fprintf(stderr, "  -X          Force output in binary\n");
805         fprintf(stderr, "  -b bytes    Number of bytes to output (default %d)\n",
806             OUTPUT_SEED_SIZE);
807 }
808
809 int
810 main(int argc, char **argv)
811 {
812         unsigned char *buf;
813         int ret, ch, debug_level, output_hex, bytes;
814         extern char *optarg;
815         LogLevel ll;
816
817         __progname = ssh_get_progname(argv[0]);
818         log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
819
820         ll = SYSLOG_LEVEL_INFO;
821         debug_level = output_hex = 0;
822         bytes = OUTPUT_SEED_SIZE;
823
824         /* Don't write binary data to a tty, unless we are forced to */
825         if (isatty(STDOUT_FILENO))
826                 output_hex = 1;
827
828         while ((ch = getopt(argc, argv, "vxXhb:")) != -1) {
829                 switch (ch) {
830                 case 'v':
831                         if (debug_level < 3)
832                                 ll = SYSLOG_LEVEL_DEBUG1 + debug_level++;
833                         break;
834                 case 'x':
835                         output_hex = 1;
836                         break;
837                 case 'X':
838                         output_hex = 0;
839                         break;
840                 case 'b':
841                         if ((bytes = atoi(optarg)) <= 0)
842                                 fatal("Invalid number of output bytes");
843                         break;
844                 case 'h':
845                         usage();
846                         exit(0);
847                 default:
848                         error("Invalid commandline option");
849                         usage();
850                 }
851         }
852
853         log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
854
855 #ifdef USE_SEED_FILES
856         prng_read_seedfile();
857 #endif
858
859         buf = xmalloc(bytes);
860
861         /*
862          * Seed the RNG from wherever we can
863          */
864
865         /* Take whatever is on the stack, but don't credit it */
866         RAND_add(buf, bytes, 0);
867
868         debug("Seeded RNG with %i bytes from system calls",
869             (int)stir_from_system());
870
871         /* try prngd, fall back to commands if prngd fails or not configured */
872         if (seed_from_prngd(buf, bytes) == 0) {
873                 RAND_add(buf, bytes, bytes);
874         } else {
875                 /* Read in collection commands */
876                 if (prng_read_commands(SSH_PRNG_COMMAND_FILE) == -1)
877                         fatal("PRNG initialisation failed -- exiting.");
878                 debug("Seeded RNG with %i bytes from programs",
879                     (int)stir_from_programs());
880         }
881
882 #ifdef USE_SEED_FILES
883         prng_write_seedfile();
884 #endif
885
886         /*
887          * Write the seed to stdout
888          */
889
890         if (!RAND_status())
891                 fatal("Not enough entropy in RNG");
892
893         if (RAND_bytes(buf, bytes) <= 0)
894                 fatal("Couldn't extract entropy from PRNG");
895
896         if (output_hex) {
897                 for(ret = 0; ret < bytes; ret++)
898                         printf("%02x", (unsigned char)(buf[ret]));
899                 printf("\n");
900         } else
901                 ret = atomicio(vwrite, STDOUT_FILENO, buf, bytes);
902
903         memset(buf, '\0', bytes);
904         xfree(buf);
905
906         return ret == bytes ? 0 : 1;
907 }
908
909 /*
910  * We may attempt to re-seed during mkstemp if we are using the one in the
911  * compat library (via mkstemp -> _gettemp -> arc4random -> seed_rng) so we
912  * need our own seed_rng().  We must also check that we have enough entropy.
913  */
914 void
915 seed_rng(void)
916 {
917         if (!RAND_status())
918                 fatal("Not enough entropy in RNG");
919 }
This page took 0.157887 seconds and 5 git commands to generate.