summaryrefslogtreecommitdiff
path: root/dbrandom.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbrandom.c')
-rw-r--r--dbrandom.c144
1 files changed, 90 insertions, 54 deletions
diff --git a/dbrandom.c b/dbrandom.c
index 0a55bc5..8ea61f6 100644
--- a/dbrandom.c
+++ b/dbrandom.c
@@ -49,24 +49,19 @@ static int donerandinit = 0;
*
*/
-/* 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 @@ process_file(hash_state *hs, const char *filename,
}
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 @@ void fuzz_seed(void) {
}
#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 @@ void seedrandom() {
pid_t pid;
struct timeval tv;
clock_t clockval;
+ int urandom_seeded = 0;
#if DROPBEAR_FUZZ
if (fuzz.fuzzing) {
@@ -215,20 +241,30 @@ void seedrandom() {
/* 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);
+#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);
- }
+ /* 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 */