source: trunk/third/openssh/ssh-rand-helper.c @ 18759

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