📄 ssh-rand-helper.c
字号:
/* * Copyright (c) 2001-2002 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "includes.h"#include <openssl/rand.h>#include <openssl/sha.h>#include <openssl/crypto.h>/* SunOS 4.4.4 needs this */#ifdef HAVE_FLOATINGPOINT_H# include <floatingpoint.h>#endif /* HAVE_FLOATINGPOINT_H */#include "misc.h"#include "xmalloc.h"#include "atomicio.h"#include "pathnames.h"#include "log.h"RCSID("$Id: ssh-rand-helper.c,v 1.7 2002/06/09 19:41:49 mouring Exp $");/* Number of bytes we write out */#define OUTPUT_SEED_SIZE 48/* Length of on-disk seedfiles */#define SEED_FILE_SIZE 1024/* Maximum number of command-line arguments to read from file */#define NUM_ARGS 10/* Minimum number of usable commands to be considered sufficient */#define MIN_ENTROPY_SOURCES 16/* Path to on-disk seed file (relative to user's home directory */#ifndef SSH_PRNG_SEED_FILE# define SSH_PRNG_SEED_FILE _PATH_SSH_USER_DIR"/prng_seed"#endif/* Path to PRNG commands list */#ifndef SSH_PRNG_COMMAND_FILE# define SSH_PRNG_COMMAND_FILE SSHDIR "/ssh_prng_cmds"#endif#ifdef HAVE___PROGNAMEextern char *__progname;#elsechar *__progname;#endif#ifndef offsetof# define offsetof(type, member) ((size_t) &((type *)0)->member)#endif#define WHITESPACE " \t\n"#ifndef RUSAGE_SELF# define RUSAGE_SELF 0#endif#ifndef RUSAGE_CHILDREN# define RUSAGE_CHILDREN 0#endif#if !defined(PRNGD_SOCKET) && !defined(PRNGD_PORT)# define USE_SEED_FILES#endiftypedef struct { /* Proportion of data that is entropy */ double rate; /* Counter goes positive if this command times out */ unsigned int badness; /* Increases by factor of two each timeout */ unsigned int sticky_badness; /* Path to executable */ char *path; /* argv to pass to executable */ char *args[NUM_ARGS]; /* XXX: arbitrary limit */ /* full command string (debug) */ char *cmdstring;} entropy_cmd_t;/* slow command timeouts (all in milliseconds) *//* static int entropy_timeout_default = ENTROPY_TIMEOUT_MSEC; */static int entropy_timeout_current = ENTROPY_TIMEOUT_MSEC;/* this is initialised from a file, by prng_read_commands() */static entropy_cmd_t *entropy_cmds = NULL;/* Prototypes */double stir_from_system(void);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, 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 * supplied. * Returns 0 on success, -1 on error */intget_random_bytes_prngd(unsigned char *buf, int len, unsigned short tcp_port, char *socket_path){ int fd, addr_len, rval, errors; char msg[2]; struct sockaddr_storage addr; struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; struct sockaddr_un *addr_un = (struct sockaddr_un *)&addr; mysig_t old_sigpipe; /* Sanity checks */ if (socket_path == NULL && tcp_port == 0) fatal("You must specify a port or a socket"); if (socket_path != NULL && strlen(socket_path) >= sizeof(addr_un->sun_path)) fatal("Random pool path is too long"); if (len > 255) fatal("Too many bytes to read from PRNGD"); memset(&addr, '\0', sizeof(addr)); if (tcp_port != 0) { addr_in->sin_family = AF_INET; addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr_in->sin_port = htons(tcp_port); addr_len = sizeof(*addr_in); } else { addr_un->sun_family = AF_UNIX; strlcpy(addr_un->sun_path, socket_path, sizeof(addr_un->sun_path)); addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path) + 1; } old_sigpipe = mysignal(SIGPIPE, SIG_IGN); errors = 0; rval = -1;reopen: fd = socket(addr.ss_family, SOCK_STREAM, 0); if (fd == -1) { error("Couldn't create socket: %s", strerror(errno)); goto done; } if (connect(fd, (struct sockaddr*)&addr, addr_len) == -1) { if (tcp_port != 0) { error("Couldn't connect to PRNGD port %d: %s", tcp_port, strerror(errno)); } else { error("Couldn't connect to PRNGD socket \"%s\": %s", addr_un->sun_path, strerror(errno)); } goto done; } /* Send blocking read request to PRNGD */ msg[0] = 0x02; msg[1] = len; if (atomicio(write, fd, msg, sizeof(msg)) != sizeof(msg)) { if (errno == EPIPE && errors < 10) { close(fd); errors++; goto reopen; } error("Couldn't write to PRNGD socket: %s", strerror(errno)); goto done; } if (atomicio(read, fd, buf, len) != len) { if (errno == EPIPE && errors < 10) { close(fd); errors++; goto reopen; } error("Couldn't read from PRNGD socket: %s", strerror(errno)); goto done; } rval = 0;done: mysignal(SIGPIPE, old_sigpipe); if (fd != -1) close(fd); return rval;}doublestir_gettimeofday(double entropy_estimate){ struct timeval tv; if (gettimeofday(&tv, NULL) == -1) fatal("Couldn't gettimeofday: %s", strerror(errno)); RAND_add(&tv, sizeof(tv), entropy_estimate); return entropy_estimate;}doublestir_clock(double entropy_estimate){#ifdef HAVE_CLOCK clock_t c; c = clock(); RAND_add(&c, sizeof(c), entropy_estimate); return entropy_estimate;#else /* _HAVE_CLOCK */ return 0;#endif /* _HAVE_CLOCK */}doublestir_rusage(int who, double entropy_estimate){#ifdef HAVE_GETRUSAGE struct rusage ru; if (getrusage(who, &ru) == -1) return 0; RAND_add(&ru, sizeof(ru), entropy_estimate); return entropy_estimate;#else /* _HAVE_GETRUSAGE */ return 0;#endif /* _HAVE_GETRUSAGE */}static inttimeval_diff(struct timeval *t1, struct timeval *t2){ int secdiff, usecdiff; secdiff = t2->tv_sec - t1->tv_sec; usecdiff = (secdiff*1000000) + (t2->tv_usec - t1->tv_usec); return (int)(usecdiff / 1000);}doublehash_command_output(entropy_cmd_t *src, char *hash){ char buf[8192]; fd_set rdset; int bytes_read, cmd_eof, error_abort, msec_elapsed, p[2]; int status, total_bytes_read; static int devnull = -1; pid_t pid; SHA_CTX sha; struct timeval tv_start, tv_current; debug3("Reading output from \'%s\'", src->cmdstring); if (devnull == -1) { devnull = open("/dev/null", O_RDWR); if (devnull == -1) fatal("Couldn't open /dev/null: %s", strerror(errno)); } if (pipe(p) == -1) fatal("Couldn't open pipe: %s", strerror(errno)); (void)gettimeofday(&tv_start, NULL); /* record start time */ switch (pid = fork()) { case -1: /* Error */ close(p[0]); close(p[1]); fatal("Couldn't fork: %s", strerror(errno)); /* NOTREACHED */ case 0: /* Child */ dup2(devnull, STDIN_FILENO); dup2(p[1], STDOUT_FILENO); dup2(p[1], STDERR_FILENO); close(p[0]); close(p[1]); close(devnull); execv(src->path, (char**)(src->args)); debug("(child) Couldn't exec '%s': %s", src->cmdstring, strerror(errno)); _exit(-1); default: /* Parent */ break; } RAND_add(&pid, sizeof(&pid), 0.0); close(p[1]); /* Hash output from child */ SHA1_Init(&sha); cmd_eof = error_abort = msec_elapsed = total_bytes_read = 0; while (!error_abort && !cmd_eof) { int ret; struct timeval tv; int msec_remaining; (void) gettimeofday(&tv_current, 0); msec_elapsed = timeval_diff(&tv_start, &tv_current); if (msec_elapsed >= entropy_timeout_current) { error_abort=1; continue; } msec_remaining = entropy_timeout_current - msec_elapsed; FD_ZERO(&rdset); FD_SET(p[0], &rdset); tv.tv_sec = msec_remaining / 1000; tv.tv_usec = (msec_remaining % 1000) * 1000; ret = select(p[0] + 1, &rdset, NULL, NULL, &tv); RAND_add(&tv, sizeof(tv), 0.0); switch (ret) { case 0: /* timer expired */ error_abort = 1; break; case 1: /* command input */ do { bytes_read = read(p[0], buf, sizeof(buf)); } while (bytes_read == -1 && errno == EINTR); RAND_add(&bytes_read, sizeof(&bytes_read), 0.0); if (bytes_read == -1) { error_abort = 1; break; } else if (bytes_read) { SHA1_Update(&sha, buf, bytes_read); total_bytes_read += bytes_read; } else { cmd_eof = 1; } break; case -1: default: /* error */ debug("Command '%s': select() failed: %s", src->cmdstring, strerror(errno)); error_abort = 1; break; } } SHA1_Final(hash, &sha); close(p[0]); debug3("Time elapsed: %d msec", msec_elapsed); if (waitpid(pid, &status, 0) == -1) { error("Couldn't wait for child '%s' completion: %s", src->cmdstring, strerror(errno)); return 0.0; } RAND_add(&status, sizeof(&status), 0.0); 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 */ debug2("Command '%s' timed out", src->cmdstring); src->sticky_badness *= 2; src->badness = src->sticky_badness; return total_bytes_read; } if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { return total_bytes_read; } else { debug2("Command '%s' exit status was %d", src->cmdstring, WEXITSTATUS(status)); src->badness = src->sticky_badness = 128; return 0.0; } } else if (WIFSIGNALED(status)) { debug2("Command '%s' returned on uncaught signal %d !", src->cmdstring, status); src->badness = src->sticky_badness = 128; return 0.0; } else return 0.0;}doublestir_from_system(void){ double total_entropy_estimate;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -