X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb/blobdiff_plain/cbc993efd7c38bb71bb179feed22d440474f4f36..6d4416ccb0bbe02105c4b9dfa1442b9198c0d923:/rand/noise.c diff --git a/rand/noise.c b/rand/noise.c index fb4c4531..2c30c13c 100644 --- a/rand/noise.c +++ b/rand/noise.c @@ -29,6 +29,7 @@ #include "config.h" +#include #include #include #include @@ -46,8 +47,14 @@ # include #endif +#if defined(HAVE_LINUX_RANDOM_H) +# include +# include +#endif + #include #include +#include #include #include "noise.h" @@ -86,8 +93,8 @@ static gid_t noise_gid = NOISE_NOSETGID; /* Gid to set to spawn processes */ static int bitcount(unsigned long x) { - char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3, - 1, 2, 2, 3, 2, 3, 3, 4 }; + static const char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; int count = 0; while (x) { count += ctab[x & 0xfu]; @@ -156,11 +163,57 @@ int noise_timer(rand_pool *r) int noise_devrandom(rand_pool *r) { - int fd; + int fd = -1; octet buf[RAND_POOLSZ]; ssize_t len; size_t n = 0; int ret = 0; +#ifdef __linux__ + fd_set infd; + struct timeval tv = { 0, 0 }; +#endif +#ifdef HAVE_GETENTROPY + size_t nn; +#endif + +#if defined(HAVE_LINUX_RANDOM_H) && \ + defined(GRND_NONBLOCK) && \ + defined(SYS_getrandom) + /* --- Use the new shinies if available --- */ + + while (n < sizeof(buf)) { + if ((len = syscall(SYS_getrandom, buf + n, sizeof(buf) - n, + GRND_NONBLOCK)) <= 0) { + if (errno == ENOSYS) break; + else goto done; + } + n += len; + } + if (n == sizeof(buf)) goto win; +#endif + +#ifdef HAVE_GETENTROPY + /* --- OpenBSD-flavoured shinies --- */ + + while (n < sizeof(buf)) { + nn = sizeof(buf) - nn; + if (nn > 256) nn = 256; + if (getentropy(buf + n, nn)) break; + n += nn; + } + if (n == sizeof(buf)) goto win; +#endif + +#ifdef __linux__ + /* --- Don't take from `/dev/urandom' if `/dev/random' would block --- */ + + if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) < 0) goto done; + FD_ZERO(&infd); + FD_SET(fd, &infd); + if (select(fd + 1, &infd, 0, 0, &tv) < 0 || !FD_ISSET(fd, &infd)) + goto done; + close(fd); fd = -1; +#endif /* --- Be nice to other clients of the random device --- * * @@ -170,18 +223,24 @@ int noise_devrandom(rand_pool *r) * needs to get some more entropy from somewhere. */ - if ((fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 || + if (fd >= 0 || + (fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 || (fd = open("/dev/arandom", O_RDONLY | O_NONBLOCK)) >= 0 || (fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0) { while (n < sizeof(buf)) { if ((len = read(fd, buf + n, sizeof(buf) - n)) <= 0) break; n += len; } - rand_add(r, buf, n, n * 8); - BURN(buf); - if (n == sizeof(buf)) ret = 1; - close(fd); + if (n == sizeof(buf)) goto win; } + goto done; + +win: + ret = 1; +done: + if (fd >= 0) close(fd); + rand_add(r, buf, n, 8*n); + BURN(buf); noise_timer(r); return (ret); } @@ -228,13 +287,43 @@ void noise_setid(uid_t uid, gid_t gid) * This interface is Unix-specific. */ +struct noisekid { + rand_pool *r; + int good; + char buf[4096]; + int donep; + int ret; +}; + +static void kid_read(int fd, unsigned mode, void *p) +{ + struct noisekid *nk = p; + ssize_t sz; + int goodbits; + + noise_timer(nk->r); + if ((sz = read(fd, nk->buf, sizeof(nk->buf))) <= 0) + nk->donep = 1; + else { + goodbits = (sz * nk->good) / 128; + rand_add(nk->r, nk->buf, sz, goodbits); + nk->ret = 1; + } +} + +static void kid_dead(struct timeval *tv, void *p) + { struct noisekid *nk = p; nk->donep = 1; } + int noise_filter(rand_pool *r, int good, const char *c) { - char buf[4096]; pid_t kid; int fd[2]; struct timeval dead; int ret = 0; + struct noisekid nk = { 0 }; + sel_state sel; + sel_file sf; + sel_timer st; const char *env[] = { "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc", 0 @@ -298,39 +387,13 @@ int noise_filter(rand_pool *r, int good, const char *c) /* --- Sort out my end of the deal --- */ close(fd[1]); - - /* --- Decide on the deadline --- */ - + sel_init(&sel); + nk.r = r; nk.good = good; TV_ADDL(&dead, &dead, 0, NOISE_KIDLIFE); - - /* --- Now read, and think --- */ - - for (;;) { - struct timeval now, diff; - fd_set rd; - - gettimeofday(&now, 0); - timer(r, &now); - if (TV_CMP(&now, >, &dead)) - break; - TV_SUB(&diff, &dead, &now); - - FD_ZERO(&rd); - FD_SET(fd[0], &rd); - - if (select(fd[0] + 1, &rd, 0, 0, &diff) < 0) - break; - if (FD_ISSET(fd[0], &rd)) { - ssize_t sz; - int goodbits; - - if ((sz = read(fd[0], buf, sizeof(buf))) <= 0) - break; - goodbits = (sz * good) / 128; - rand_add(r, buf, sz, goodbits); - ret = 1; - } - } + sel_initfile(&sel, &sf, fd[0], SEL_READ, kid_read, &nk); + sel_addfile(&sf); + sel_addtimer(&sel, &st, &dead, kid_dead, &nk); + while (!nk.donep && !sel_select(&sel)); /* --- We've finished with it: kill the child process --- * * @@ -340,11 +403,11 @@ int noise_filter(rand_pool *r, int good, const char *c) */ close(fd[0]); - BURN(buf); + BURN(nk.buf); noise_timer(r); kill(kid, SIGKILL); waitpid(kid, 0, 0); - return (ret); + return (nk.ret); } /* --- @noise_freewheel@ --- * @@ -360,7 +423,7 @@ int noise_filter(rand_pool *r, int good, const char *c) #ifdef USE_FREEWHEEL -static jmp_buf fwjmp; +static sigjmp_buf fwjmp; static void fwalarm(int sig) {