X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/49d7ed327de44e037d0fe19c58617accfb4e5583..34f2baf0f5d9dad08aaed92f16ee660b9c150d65:/ssh-rand-helper.c diff --git a/ssh-rand-helper.c b/ssh-rand-helper.c index d5de2946..2d8707ee 100644 --- a/ssh-rand-helper.c +++ b/ssh-rand-helper.c @@ -60,19 +60,10 @@ RCSID("$Id$"); /* Path to PRNG commands list */ #ifndef SSH_PRNG_COMMAND_FILE -# define SSH_PRNG_COMMAND_FILE ETCDIR "/ssh_prng_cmds" +# define SSH_PRNG_COMMAND_FILE SSHDIR "/ssh_prng_cmds" #endif - -#ifdef HAVE___PROGNAME extern char *__progname; -#else -char *__progname; -#endif - -#ifndef offsetof -# define offsetof(type, member) ((size_t) &((type *)0)->member) -#endif #define WHITESPACE " \t\n" @@ -115,20 +106,20 @@ double stir_from_programs(void); double stir_gettimeofday(double entropy_estimate); double stir_clock(double entropy_estimate); double stir_rusage(int who, double entropy_estimate); -double hash_command_output(entropy_cmd_t *src, char *hash); -int get_random_bytes_prngd(unsigned char *buf, int len, +double hash_command_output(entropy_cmd_t *src, unsigned char *hash); +int get_random_bytes_prngd(unsigned char *buf, int len, unsigned short tcp_port, char *socket_path); /* * Collect 'len' bytes of entropy into 'buf' from PRNGD/EGD daemon * listening either on 'tcp_port', or via Unix domain socket at * * 'socket_path'. - * Either a non-zero tcp_port or a non-null socket_path must be + * Either a non-zero tcp_port or a non-null socket_path must be * supplied. * Returns 0 on success, -1 on error */ int -get_random_bytes_prngd(unsigned char *buf, int len, +get_random_bytes_prngd(unsigned char *buf, int len, unsigned short tcp_port, char *socket_path) { int fd, addr_len, rval, errors; @@ -188,7 +179,7 @@ reopen: msg[0] = 0x02; msg[1] = len; - if (atomicio(write, fd, msg, sizeof(msg)) != sizeof(msg)) { + if (atomicio(vwrite, fd, msg, sizeof(msg)) != sizeof(msg)) { if (errno == EPIPE && errors < 10) { close(fd); errors++; @@ -274,7 +265,7 @@ timeval_diff(struct timeval *t1, struct timeval *t2) } double -hash_command_output(entropy_cmd_t *src, char *hash) +hash_command_output(entropy_cmd_t *src, unsigned char *hash) { char buf[8192]; fd_set rdset; @@ -290,7 +281,7 @@ hash_command_output(entropy_cmd_t *src, char *hash) if (devnull == -1) { devnull = open("/dev/null", O_RDWR); if (devnull == -1) - fatal("Couldn't open /dev/null: %s", + fatal("Couldn't open /dev/null: %s", strerror(errno)); } @@ -315,7 +306,7 @@ hash_command_output(entropy_cmd_t *src, char *hash) execv(src->path, (char**)(src->args)); - debug("(child) Couldn't exec '%s': %s", + debug("(child) Couldn't exec '%s': %s", src->cmdstring, strerror(errno)); _exit(-1); default: /* Parent */ @@ -356,6 +347,7 @@ hash_command_output(entropy_cmd_t *src, char *hash) case 0: /* timer expired */ error_abort = 1; + kill(pid, SIGINT); break; case 1: /* command input */ @@ -376,7 +368,7 @@ hash_command_output(entropy_cmd_t *src, char *hash) case -1: default: /* error */ - debug("Command '%s': select() failed: %s", + debug("Command '%s': select() failed: %s", src->cmdstring, strerror(errno)); error_abort = 1; break; @@ -391,7 +383,7 @@ hash_command_output(entropy_cmd_t *src, char *hash) if (waitpid(pid, &status, 0) == -1) { error("Couldn't wait for child '%s' completion: %s", - src->cmdstring, strerror(errno)); + src->cmdstring, strerror(errno)); return 0.0; } @@ -400,8 +392,8 @@ hash_command_output(entropy_cmd_t *src, char *hash) if (error_abort) { /* * Closing p[0] on timeout causes the entropy command to - * SIGPIPE. Take whatever output we got, and mark this - * command as slow + * SIGPIPE. Take whatever output we got, and mark this + * command as slow */ debug2("Command '%s' timed out", src->cmdstring); src->sticky_badness *= 2; @@ -460,7 +452,7 @@ stir_from_programs(void) { int c; double entropy, total_entropy; - char hash[SHA_DIGEST_LENGTH]; + unsigned char hash[SHA_DIGEST_LENGTH]; total_entropy = 0; for(c = 0; entropy_cmds[c].path != NULL; c++) { @@ -479,7 +471,7 @@ stir_from_programs(void) /* Stir it in */ RAND_add(hash, sizeof(hash), entropy); - debug3("Got %0.2f bytes of entropy from '%s'", + debug3("Got %0.2f bytes of entropy from '%s'", entropy, entropy_cmds[c].cmdstring); total_entropy += entropy; @@ -491,7 +483,7 @@ stir_from_programs(void) total_entropy += stir_rusage(RUSAGE_CHILDREN, 0.1); } else { debug2("Command '%s' disabled (badness %d)", - entropy_cmds[c].cmdstring, + entropy_cmds[c].cmdstring, entropy_cmds[c].badness); if (entropy_cmds[c].badness > 0) @@ -511,8 +503,8 @@ prng_check_seedfile(char *filename) struct stat st; /* - * XXX raceable: eg replace seed between this stat and subsequent - * open. Not such a problem because we don't really trust the + * XXX raceable: eg replace seed between this stat and subsequent + * open. Not such a problem because we don't really trust the * seed file anyway. * XXX: use secure path checking as elsewhere in OpenSSH */ @@ -532,7 +524,7 @@ prng_check_seedfile(char *filename) /* mode 0600, owned by root or the current user? */ if (((st.st_mode & 0177) != 0) || !(st.st_uid == getuid())) { debug("WARNING: PRNG seedfile %.100s must be mode 0600, " - "owned by uid %d", filename, getuid()); + "owned by uid %li", filename, (long int)getuid()); return 0; } @@ -543,13 +535,14 @@ void prng_write_seedfile(void) { int fd; - char seed[SEED_FILE_SIZE], filename[MAXPATHLEN]; + unsigned char seed[SEED_FILE_SIZE]; + char filename[MAXPATHLEN]; struct passwd *pw; pw = getpwuid(getuid()); if (pw == NULL) fatal("Couldn't get password entry for current user " - "(%i): %s", getuid(), strerror(errno)); + "(%li): %s", (long int)getuid(), strerror(errno)); /* Try to ensure that the parent directory is there */ snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, @@ -561,7 +554,8 @@ prng_write_seedfile(void) debug("writing PRNG seed to file %.100s", filename); - RAND_bytes(seed, sizeof(seed)); + if (RAND_bytes(seed, sizeof(seed)) <= 0) + fatal("PRNG seed extraction failed"); /* Don't care if the seed doesn't exist */ prng_check_seedfile(filename); @@ -570,7 +564,7 @@ prng_write_seedfile(void) debug("WARNING: couldn't access PRNG seedfile %.100s " "(%.100s)", filename, strerror(errno)); } else { - if (atomicio(write, fd, &seed, sizeof(seed)) < sizeof(seed)) + if (atomicio(vwrite, fd, &seed, sizeof(seed)) < sizeof(seed)) fatal("problem writing PRNG seedfile %.100s " "(%.100s)", filename, strerror(errno)); close(fd); @@ -587,7 +581,7 @@ prng_read_seedfile(void) pw = getpwuid(getuid()); if (pw == NULL) fatal("Couldn't get password entry for current user " - "(%i): %s", getuid(), strerror(errno)); + "(%li): %s", (long int)getuid(), strerror(errno)); snprintf(filename, sizeof(filename), "%.512s/%s", pw->pw_dir, SSH_PRNG_SEED_FILE); @@ -649,7 +643,7 @@ prng_read_commands(char *cmdfilename) continue; /* done with this line */ /* - * The first non-whitespace char should be a double quote + * The first non-whitespace char should be a double quote * delimiting the commandline */ if (*cp != '"') { @@ -724,7 +718,7 @@ prng_read_commands(char *cmdfilename) /* * If we've filled the array, reallocate it twice the size - * Do this now because even if this we're on the last + * Do this now because even if this we're on the last * command we need another slot to mark the last entry */ if (cur_cmd == num_cmds) { @@ -747,43 +741,93 @@ prng_read_commands(char *cmdfilename) return cur_cmd < MIN_ENTROPY_SOURCES ? -1 : 0; } -int +void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n", __progname); + fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); + fprintf(stderr, " Multiple -v increases verbosity.\n"); + fprintf(stderr, " -x Force output in hexidecimal (for debugging)\n"); + fprintf(stderr, " -X Force output in binary\n"); + fprintf(stderr, " -b bytes Number of bytes to output (default %d)\n", + OUTPUT_SEED_SIZE); +} + +int main(int argc, char **argv) { - unsigned char buf[OUTPUT_SEED_SIZE]; - int ret; + unsigned char *buf; + int ret, ch, debug_level, output_hex, bytes; + extern char *optarg; + LogLevel ll; - __progname = get_progname(argv[0]); - /* XXX: need some debugging mode */ + __progname = ssh_get_progname(argv[0]); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); + ll = SYSLOG_LEVEL_INFO; + debug_level = output_hex = 0; + bytes = OUTPUT_SEED_SIZE; + + /* Don't write binary data to a tty, unless we are forced to */ + if (isatty(STDOUT_FILENO)) + output_hex = 1; + + while ((ch = getopt(argc, argv, "vxXhb:")) != -1) { + switch (ch) { + case 'v': + if (debug_level < 3) + ll = SYSLOG_LEVEL_DEBUG1 + debug_level++; + break; + case 'x': + output_hex = 1; + break; + case 'X': + output_hex = 0; + break; + case 'b': + if ((bytes = atoi(optarg)) <= 0) + fatal("Invalid number of output bytes"); + break; + case 'h': + usage(); + exit(0); + default: + error("Invalid commandline option"); + usage(); + } + } + + log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); + #ifdef USE_SEED_FILES prng_read_seedfile(); #endif + buf = xmalloc(bytes); + /* * Seed the RNG from wherever we can */ - + /* Take whatever is on the stack, but don't credit it */ - RAND_add(buf, sizeof(buf), 0); + RAND_add(buf, bytes, 0); - debug("Seeded RNG with %i bytes from system calls", + debug("Seeded RNG with %i bytes from system calls", (int)stir_from_system()); #ifdef PRNGD_PORT - if (get_random_bytes_prngd(buf, sizeof(buf), PRNGD_PORT, NULL) == -1) + if (get_random_bytes_prngd(buf, bytes, PRNGD_PORT, NULL) == -1) fatal("Entropy collection failed"); - RAND_add(buf, sizeof(buf), sizeof(buf)); + RAND_add(buf, bytes, bytes); #elif defined(PRNGD_SOCKET) - if (get_random_bytes_prngd(buf, sizeof(buf), 0, PRNGD_SOCKET) == -1) + if (get_random_bytes_prngd(buf, bytes, 0, PRNGD_SOCKET) == -1) fatal("Entropy collection failed"); - RAND_add(buf, sizeof(buf), sizeof(buf)); + RAND_add(buf, bytes, bytes); #else /* Read in collection commands */ if (prng_read_commands(SSH_PRNG_COMMAND_FILE) == -1) fatal("PRNG initialisation failed -- exiting."); - debug("Seeded RNG with %i bytes from programs", + debug("Seeded RNG with %i bytes from programs", (int)stir_from_programs()); #endif @@ -798,12 +842,18 @@ main(int argc, char **argv) if (!RAND_status()) fatal("Not enough entropy in RNG"); - RAND_bytes(buf, sizeof(buf)); + if (RAND_bytes(buf, bytes) <= 0) + fatal("Couldn't extract entropy from PRNG"); - ret = atomicio(write, STDOUT_FILENO, buf, sizeof(buf)); - - memset(buf, '\0', sizeof(buf)); - - return ret == sizeof(buf) ? 0 : 1; -} + if (output_hex) { + for(ret = 0; ret < bytes; ret++) + printf("%02x", (unsigned char)(buf[ret])); + printf("\n"); + } else + ret = atomicio(vwrite, STDOUT_FILENO, buf, bytes); + + memset(buf, '\0', bytes); + xfree(buf); + return ret == bytes ? 0 : 1; +}