#include "config.h"
+#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
# include <grp.h>
#endif
+#if defined(HAVE_LINUX_RANDOM_H)
+# include <linux/random.h>
+# include <sys/syscall.h>
+#endif
+
#include <mLib/bits.h>
#include <mLib/mdup.h>
+#include <mLib/sel.h>
#include <mLib/tv.h>
#include "noise.h"
/*----- Magical numbers ---------------------------------------------------*/
#define NOISE_KIDLIFE 100000 /* @noise_filter@ child lifetime */
-#define MILLION 1000000 /* One million */
+
+#if HAVE_CLOCK_GETTIME && _POSIX_TIMERS > 0
+# define TIMESTRUCT timespec
+# define tv_SEC tv_sec
+# define tv_FRAC tv_nsec
+# define TIMERES 1000000000
+# if _POSIX_MONOTONIC_CLOCK > 0
+# define GETTIME(tv) (clock_gettime(CLOCK_MONOTONIC, (tv)))
+# else
+# define GETTIME(tv) (clock_gettime(CLOCK_REALTIME, (tv)))
+# endif
+# define TOTIMEVAL(tv, xx) \
+ ((tv)->tv_sec = (xx)->tv_sec, \
+ (tv)->tv_usec = ((xx)->tv_nsec + 500)/1000)
+#else
+# define TIMESTRUCT timeval
+# define tv_SEC tv_sec
+# define tv_FRAC tv_usec
+# define TIMERES 1000000
+# define GETTIME(tv) (gettimeofday((tv), 0))
+# define TOTIMEVAL(tv, xx) (*(tv) = *(xx))
+#endif
/*----- Noise source definition -------------------------------------------*/
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];
/* --- @timer@ --- *
*
* Arguments: @rand_pool *r@ = pointer to randomness pool
- * @struct timeval *tv@ = pointer to time block
+ * @const struct TIMESTRUCT *tv@ = pointer to time block
*
* Returns: Nonzero if some randomness was contributed.
*
* Use: Low-level timer contributor.
*/
-static int timer(rand_pool *r, struct timeval *tv)
+static int timer(rand_pool *r, const struct TIMESTRUCT *tv)
{
unsigned long x, d, dd;
int de, dde;
int ret;
- x = tv->tv_usec + MILLION * tv->tv_sec;
+ x = tv->tv_FRAC + TIMERES*tv->tv_SEC;
d = x ^ noise_last;
dd = d ^ noise_diff;
noise_last = x;
int noise_timer(rand_pool *r)
{
- struct timeval tv;
- gettimeofday(&tv, 0);
- return (timer(r, &tv));
+ struct TIMESTRUCT tv;
+ GETTIME(&tv); return (timer(r, &tv));
}
/* --- @noise_devrandom@ --- *
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) - n;
+ 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 --- *
*
* 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);
}
* 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;
+ struct TIMESTRUCT now;
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
/* --- Remember when this business started --- */
- gettimeofday(&dead, 0);
- timer(r, &dead);
+ GETTIME(&now); timer(r, &now);
+ TOTIMEVAL(&dead, &now);
/* --- Create a pipe --- */
/* --- Play games with uids --- */
if (noise_gid != NOISE_NOSETGID) {
- setgid(noise_gid);
- setegid(noise_gid);
+ if (setgid(noise_gid) || setegid(noise_gid)
#ifdef HAVE_SETGROUPS
- setgroups(1, &noise_gid);
+ || setgroups(1, &noise_gid)
#endif
+ ) _exit(127);
}
if (noise_uid != NOISE_NOSETUID) {
- setuid(noise_uid);
- seteuid(noise_uid);
+ if (setuid(noise_uid) || seteuid(noise_uid)) _exit(127);
}
/* --- Start the process up --- */
/* --- 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 --- *
*
*/
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@ --- *
#ifdef USE_FREEWHEEL
-static jmp_buf fwjmp;
+static sigjmp_buf fwjmp;
static void fwalarm(int sig)
{