Mercurial > dropbear
diff dbrandom.c @ 1698:f966834f0f9c
Use Linux getrandom() to ensure random device is initialised
Remove old code warning about random device being not ready,
/dev/random isn't used by default anyway.
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 28 May 2020 22:50:41 +0800 |
parents | 60fceff95858 |
children | 6e5037ae2c1c |
line wrap: on
line diff
--- a/dbrandom.c Thu May 28 22:02:33 2020 +0800 +++ b/dbrandom.c Thu May 28 22:50:41 2020 +0800 @@ -49,24 +49,19 @@ * */ -/* Pass len=0 to hash an entire file */ +/* Pass wantlen=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; + unsigned int wantlen, int prngd) { int readfd; unsigned int readcount; int ret = DROPBEAR_FAILURE; + if (prngd) { #if DROPBEAR_USE_PRNGD - if (prngd) - { readfd = connect_unix(filename); - } - else #endif - { + } else { readfd = open(filename, O_RDONLY); } @@ -75,58 +70,31 @@ } readcount = 0; - while (len == 0 || readcount < len) - { + while (wantlen == 0 || readcount < wantlen) { int readlen, wantread; unsigned char readbuf[4096]; - if (!already_blocked && !prngd) - { - int res; - struct timeval timeout; - fd_set read_fds; - - timeout.tv_sec = 2; - timeout.tv_usec = 0; - - DROPBEAR_FD_ZERO(&read_fds); - FD_SET(readfd, &read_fds); - res = select(readfd + 1, &read_fds, NULL, NULL, &timeout); - if (res == 0) - { - 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; - } - } - - if (len == 0) - { + if (wantlen == 0) { wantread = sizeof(readbuf); - } - else - { - wantread = MIN(sizeof(readbuf), len-readcount); + } else { + wantread = MIN(sizeof(readbuf), wantlen-readcount); } #if DROPBEAR_USE_PRNGD - if (prngd) - { + if (prngd) { char egdcmd[2]; egdcmd[0] = 0x02; /* blocking read */ egdcmd[1] = (unsigned char)wantread; - if (write(readfd, egdcmd, 2) < 0) - { + 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; } - if (readlen == 0 && len == 0) - { + if (readlen == 0 && wantlen == 0) { /* whole file was read as requested */ break; } @@ -193,6 +161,63 @@ } #endif + +#ifdef HAVE_GETRANDOM +/* Reads entropy seed with getrandom(). + * May block if the kernel isn't ready. + * Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int process_getrandom(hash_state *hs) { + char buf[INIT_SEED_SIZE]; + ssize_t ret; + + /* First try non-blocking so that we can warn about waiting */ + ret = getrandom(buf, sizeof(buf), GRND_NONBLOCK); + if (ret == -1) { + if (errno == ENOSYS) { + /* Old kernel */ + return DROPBEAR_FAILURE; + } + /* Other errors fall through to blocking getrandom() */ + TRACE(("first getrandom() failed: %d %s", errno, strerror(errno))) + if (errno == EAGAIN) { + dropbear_log(LOG_WARNING, "Waiting for kernel randomness to be initialised..."); + } + } + + /* Wait blocking if needed. Loop in case we get EINTR */ + while (ret != sizeof(buf)) { + ret = getrandom(buf, sizeof(buf), 0); + + if (ret == sizeof(buf)) { + /* Success */ + break; + } + if (ret == -1 && errno == EINTR) { + /* Try again. */ + continue; + } + if (ret >= 0) { + TRACE(("Short read %zd from getrandom() shouldn't happen", ret)) + /* Try again? */ + continue; + } + + /* Unexpected problem, fall back to /dev/urandom */ + TRACE(("2nd getrandom() failed: %d %s", errno, strerror(errno))) + break; + } + + if (ret == sizeof(buf)) { + /* Success, stir in the entropy */ + sha1_process(hs, (void*)buf, sizeof(buf)); + return DROPBEAR_SUCCESS; + } + + return DROPBEAR_FAILURE; + +} +#endif /* HAVE_GETRANDOM */ + /* Initialise the prng from /dev/urandom or prngd. This function can * be called multiple times */ void seedrandom() { @@ -202,6 +227,7 @@ pid_t pid; struct timeval tv; clock_t clockval; + int urandom_seeded = 0; #if DROPBEAR_FUZZ if (fuzz.fuzzing) { @@ -215,21 +241,31 @@ /* existing state */ sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); -#if DROPBEAR_USE_PRNGD - 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); +#ifdef HAVE_GETRANDOM + if (process_getrandom(&hs) == DROPBEAR_SUCCESS) { + urandom_seeded = 1; } #endif + if (!urandom_seeded) { +#if DROPBEAR_USE_PRNGD + if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_PRNGD_SOCKET); + urandom_seeded = 1; + } +#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); + urandom_seeded = 1; + } +#endif + } /* urandom_seeded */ + /* A few other sources to fall back on. * Add more here for other platforms */ #ifdef __linux__