diff options
-rw-r--r-- | common-runopts.c | 4 | ||||
-rw-r--r-- | gendss.c | 1 | ||||
-rw-r--r-- | genrsa.c | 1 | ||||
-rw-r--r-- | options.h | 20 | ||||
-rw-r--r-- | random.c | 209 | ||||
-rw-r--r-- | random.h | 6 | ||||
-rw-r--r-- | svr-chansession.c | 2 | ||||
-rw-r--r-- | svr-main.c | 7 | ||||
-rw-r--r-- | svr-session.c | 1 | ||||
-rw-r--r-- | sysoptions.h | 8 |
10 files changed, 161 insertions, 98 deletions
diff --git a/common-runopts.c b/common-runopts.c index 784055a..32e475e 100644 --- a/common-runopts.c +++ b/common-runopts.c @@ -29,6 +29,7 @@ #include "dbutil.h" #include "auth.h" #include "algo.h" +#include "random.h" runopts opts; /* GLOBAL */ @@ -45,6 +46,9 @@ int readhostkey(const char * filename, sign_key * hostkey, int *type) { goto out; } buf_setpos(buf, 0); + + addrandom(buf_getptr(buf, buf->len), buf->len); + if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) { goto out; } @@ -57,6 +57,7 @@ dropbear_dss_key * gen_dss_priv_key(unsigned int size) { m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL); seedrandom(); + seedstrongrandom(); getq(key); getp(key, size); @@ -56,6 +56,7 @@ dropbear_rsa_key * gen_rsa_priv_key(unsigned int size) { &pminus, &lcm, &qminus, NULL); seedrandom(); + seedstrongrandom(); if (mp_set_int(key->e, RSA_E) != MP_OKAY) { fprintf(stderr, "RSA generation failed\n"); @@ -204,21 +204,15 @@ much traffic. */ * return the password on standard output */ /*#define ENABLE_CLI_ASKPASS_HELPER*/ -/* Random device to use - define either DROPBEAR_RANDOM_DEV or - * DROPBEAR_PRNGD_SOCKET. - * DROPBEAR_RANDOM_DEV is recommended on hosts with a good /dev/(u)random, - * otherwise use run prngd (or egd if you want), specifying the socket. - * The device will be queried for a few dozen bytes of seed a couple of times - * per session (or more for very long-lived sessions). */ - -/* We'll use /dev/urandom by default, since /dev/random is too much hassle. - * If system developers aren't keeping seeds between boots nor getting - * any entropy from somewhere it's their own fault. */ -#define DROPBEAR_RANDOM_DEV "/dev/urandom" - -/* prngd must be manually set up to produce output */ +/* Source for randomness. This must be able to provide hundreds of bytes per SSH + * connection without blocking. In addition /dev/random is used for seeding + * rsa/dss key generation */ +#define DROPBEAR_URANDOM_DEV "/dev/urandom" + +/* Set this to use PRNGD or EGD instead of /dev/urandom or /dev/random */ /*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/ + /* Specify the number of clients we will allow to be connected but * not yet authenticated. After this limit, connections are rejected */ /* The first setting is per-IP, to avoid denial of service */ @@ -27,19 +27,16 @@ #include "dbutil.h" #include "bignum.h" -static int donerandinit = 0; - /* this is used to generate unique output from the same hashpool */ static uint32_t counter = 0; /* the max value for the counter, so it won't integer overflow */ #define MAX_COUNTER 1<<30 -static unsigned char hashpool[SHA1_HASH_SIZE]; +static unsigned char hashpool[SHA1_HASH_SIZE] = {0}; +static int donerandinit = 0; #define INIT_SEED_SIZE 32 /* 256 bits */ -static void readrand(unsigned char* buf, unsigned int buflen); - /* The basic setup is we read some data from /dev/(u)random or prngd and hash it * into hashpool. To read data, we hash together current hashpool contents, * and a counter. We feed more data in by hashing the current pool and new @@ -50,120 +47,190 @@ static void readrand(unsigned char* buf, unsigned int buflen); * */ -static void readrand(unsigned char* buf, unsigned int buflen) { - +/* Pass len=0 to hash an entire file */ +static int +process_file(hash_state *hs, const char *filename, + unsigned int len, int prngd) +{ static int already_blocked = 0; int readfd; - unsigned int readpos; - int readlen; -#ifdef DROPBEAR_PRNGD_SOCKET - struct sockaddr_un egdsock; - char egdcmd[2]; -#endif + unsigned int readcount; + int ret = DROPBEAR_FAILURE; -#ifdef DROPBEAR_RANDOM_DEV - readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY); - if (readfd < 0) { - dropbear_exit("Couldn't open random device"); +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + readfd = connect_unix(filename); } + else #endif - -#ifdef DROPBEAR_PRNGD_SOCKET - readfd = connect_unix(DROPBEAR_PRNGD_SOCKET); + { + readfd = open(filename, O_RDONLY); + } if (readfd < 0) { - dropbear_exit("Couldn't open random device"); + goto out; } - if (buflen > 255) - dropbear_exit("Can't request more than 255 bytes from egd"); - egdcmd[0] = 0x02; /* blocking read */ - egdcmd[1] = (unsigned char)buflen; - if (write(readfd, egdcmd, 2) < 0) - dropbear_exit("Can't send command to egd"); -#endif - - /* read the actual random data */ - readpos = 0; - do { + readcount = 0; + while (readcount < len) + { + int readlen, wantread; + unsigned char readbuf[128]; if (!already_blocked) { int ret; - struct timeval timeout; + struct timeval timeout = { .tv_sec = 2, .tv_usec = 0}; fd_set read_fds; - timeout.tv_sec = 2; /* two seconds should be enough */ - timeout.tv_usec = 0; - FD_ZERO(&read_fds); FD_SET(readfd, &read_fds); ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout); if (ret == 0) { - dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source."); + dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename); already_blocked = 1; } } - readlen = read(readfd, &buf[readpos], buflen - readpos); + + wantread = MIN(sizeof(readbuf), len-readcount); + +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + char egdcmd[2]; + egdcmd[0] = 0x02; /* blocking read */ + egdcmd[1] = (unsigned char)wantread; + if (write(readfd, egdcmd, 2) < 0) + { + dropbear_exit("Can't send command to egd"); + } + } +#endif + + readlen = read(readfd, readbuf, wantread); if (readlen <= 0) { if (readlen < 0 && errno == EINTR) { continue; } - dropbear_exit("Error reading random source"); + if (readlen == 0 && len == 0) + { + /* whole file was read as requested */ + break; + } + goto out; } - readpos += readlen; - } while (readpos < buflen); - - close (readfd); + sha1_process(hs, readbuf, readlen); + readcount += readlen; + } + ret = DROPBEAR_SUCCESS; +out: + close(readfd); + return ret; } -/* initialise the prng from /dev/(u)random or prngd */ -void seedrandom() { - - unsigned char readbuf[INIT_SEED_SIZE]; - +void addrandom(char * buf, int len) +{ hash_state hs; - /* initialise so that things won't warn about - * hashing an undefined buffer */ - if (!donerandinit) { - m_burn(hashpool, sizeof(hashpool)); - } - - /* get the seed data */ - readrand(readbuf, sizeof(readbuf)); - /* hash in the new seed data */ sha1_init(&hs); + /* existing state (zeroes on startup) */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - sha1_process(&hs, (void*)readbuf, sizeof(readbuf)); - sha1_done(&hs, hashpool); - counter = 0; - donerandinit = 1; + /* new */ + sha1_process(&hs, buf, len); + sha1_done(&hs, hashpool); } -/* hash the current random pool with some unique identifiers - * for this process and point-in-time. this is used to separate - * the random pools for fork()ed processes. */ -void reseedrandom() { +static void write_urandom() +{ +#ifndef DROPBEAR_PRNGD_SOCKET + /* This is opportunistic, don't worry about failure */ + unsigned char buf[INIT_SEED_SIZE]; + FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); + genrandom(buf, sizeof(buf)); + fwrite(buf, sizeof(buf), 1, f); + fclose(f); +#endif +} - pid_t pid; +/* add entropy from the stronger, blocking source /dev/random. Only used + * for generating persistent private keys (RSA and DSS) */ +void seedstrongrandom() +{ + /* We assume that PRNGD is a strong source, so don't need to do anything here */ +#ifndef DROPBEAR_PRNGD_SOCKET hash_state hs; - struct timeval tv; - if (!donerandinit) { - dropbear_exit("seedrandom not done"); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + if (process_file(&hs, "/dev/random", INIT_SEED_SIZE, 0) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", "/dev/random"); } - pid = getpid(); - gettimeofday(&tv, NULL); + sha1_done(&hs, hashpool); +#endif +} + +/* Initialise the prng from /dev/urandom or prngd. This function can + * be called multiple times */ +void seedrandom() { + + hash_state hs; + + pid_t pid; + struct timeval tv; + clock_t clockval; + /* hash in the new seed data */ sha1_init(&hs); + /* existing state */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + +#ifdef DROPBEAR_PRNGD_SOCKET + if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_PRNGD_SOCKET); + } +#else + /* non-blocking random source (probably /dev/urandom) */ + if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_URANDOM_DEV); + } +#endif + + /* A few other sources to fall back on. Add more here for other platforms */ +#ifdef __linux__ + /* Seems to be a reasonable source of entropy from timers */ + process_file(&hs, "/proc/timer_list", 0, 0); + /* Might help on systems with wireless */ + process_file(&hs, "/proc/interrupts", 0, 0); +#endif + + pid = getpid(); sha1_process(&hs, (void*)&pid, sizeof(pid)); + + gettimeofday(&tv, NULL); sha1_process(&hs, (void*)&tv, sizeof(tv)); + + clockval = clock(); + sha1_process(&hs, (void*)&clockval, sizeof(clockval)); + + /* When a private key is read by the client or server it will + * be added to the hashpool - see runopts.c */ + sha1_done(&hs, hashpool); + + counter = 0; + donerandinit = 1; + + /* Feed it all back into /dev/urandom - this might help if Dropbear + * is running from inetd and gets new state each time */ + write_urandom(); } /* return len bytes of pseudo-random data */ @@ -27,10 +27,10 @@ struct mp_int; +void seedstrongrandom(); void seedrandom(); -void reseedrandom(); -void genrandom(unsigned char* buf, int len); -void addrandom(unsigned char* buf, int len); +void genrandom(unsigned char* buf, unsigned int len); +void addrandom(char * buf, unsigned int len); void gen_random_mpint(mp_int *max, mp_int *rand); #endif /* _RANDOM_H_ */ diff --git a/svr-chansession.c b/svr-chansession.c index b99bf26..88a4b85 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -871,7 +871,7 @@ static void execchild(void *user_data) { svr_opts.hostkey = NULL; /* overwrite the prng state */ - reseedrandom(); + seedrandom(); #endif /* clear environment */ @@ -254,6 +254,8 @@ void main_noinetd() { goto out; } + seedrandom(); + if (pipe(childpipe) < 0) { TRACE(("error creating child pipe")) goto out; @@ -267,8 +269,11 @@ void main_noinetd() { if (fork_ret < 0) { dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno)); goto out; + } - } else if (fork_ret > 0) { + addrandom(&fork_ret, sizeof(fork_ret)); + + if (fork_ret > 0) { /* parent */ childpipes[conn_idx] = childpipe[0]; diff --git a/svr-session.c b/svr-session.c index 9c12e0f..cf82289 100644 --- a/svr-session.c +++ b/svr-session.c @@ -75,7 +75,6 @@ static const struct ChanType *svr_chantypes[] = { void svr_session(int sock, int childpipe) { char *host, *port; size_t len; - reseedrandom(); crypto_init(); common_session_init(sock, sock); diff --git a/sysoptions.h b/sysoptions.h index e2f53e3..f927726 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -182,14 +182,6 @@ #error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h" #endif -#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET) -#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once" -#endif - -#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET) -#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h" -#endif - /* We use dropbear_client and dropbear_server as shortcuts to avoid redundant * code, if we're just compiling as client or server */ #if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT) |