Commit b6a32a1d authored by Niels Möller's avatar Niels Möller
Browse files

* src/unix_random.c: Hacked and debugged. Seems to work now.

Rev: src/unix_random.c:1.2
parent 7f77430f
......@@ -25,14 +25,49 @@
*/
#include "randomness.h"
#include "crypto.h"
#include "reaper.h"
#include "xalloc.h"
#include "werror.h"
#include <assert.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <pwd.h>
#ifdef HAVE_POLL
# if HAVE_POLL_H
# include <poll.h>
# elif HAVE_SYS_POLL_H
# include <sys/poll.h>
# endif
#else
# include "jpoll.h"
#endif
/* Workaround for some version of FreeBSD. */
#ifdef POLLRDNORM
# define MY_POLLIN (POLLIN | POLLRDNORM)
#else /* !POLLRDNORM */
# define MY_POLLIN POLLIN
#endif /* !POLLRDNORM */
#include <sys/time.h>
#include <sys/resource.h>
enum poll_status { POLL_NO_POLL, POLL_RUNNING, POLL_FINISHED, POLL_FAILED };
#include "unix_random.c.x"
/* GABA:
(class
(name unix_random)
......@@ -61,12 +96,21 @@ enum poll_status { POLL_NO_POLL, POLL_RUNNING, POLL_FINISHED, POLL_FAILED };
static void
do_unix_random_callback(struct exit_callback *s,
int signalled, int core, int value)
int signalled, int core UNUSED,
int value)
{
CAST(unix_random_callback, self, s);
self->ctx->status = (signalled || value)
? POLL_FAILED
: POLL_FINISHED;
if (signalled || value)
{
self->ctx->status = POLL_FAILED;
trace("unix_random.c: do_unix_random_callback, background poll failed.\n");
}
else
{
self->ctx->status = POLL_FINISHED;
trace("unix_random.c: do_unix_random_callback, background poll finished.\n");
}
}
static struct exit_callback *
......@@ -97,7 +141,9 @@ struct unix_random_source
const char *arg; /* For now, use at most one argument. */
int has_alternative;
unsigned rounding; /* Add this before rounding */
/* If non-zero, count quality bits of entropy if the amount of output
* exceeds this value. */
unsigned small;
/* Bits of entropy per UNIX_RANDOM_SOURCE_SCALE bytes of output. */
unsigned quality;
};
......@@ -105,7 +151,7 @@ struct unix_random_source
/* Lots of output expected; round downwards. */
#define WLARGE(x) 0, x
/* Small but significant output expected; round upwards */
#define WSMALL(x) (UNIX_RANDOM_SOURCE_SCALE - 1), x
#define WSMALL(x) 100, x
static const struct unix_random_source random_sources[] =
{
......@@ -239,17 +285,19 @@ struct unix_random_source_state
static int
spawn_source_process(unsigned *index,
struct unix_random_source_state *state)
struct unix_random_source_state *state,
int null)
{
unsigned i;
pid_t pid;
/* output[0] for reading, output[1] for writing. */
int output[2];
for (i = *index; random_sources[i].path; )
{
int output[2];
pid_t pid;
if (access(random_sources[i].path, X_OK) < 0)
{
debug("spawn_source_process: Can't execute '%z': %z\n",
debug("unix_random.c: spawn_source_process: Skipping '%z'; not executable: %z\n",
random_sources[i].path, STRERROR(errno));
i++;
}
......@@ -257,9 +305,15 @@ spawn_source_process(unsigned *index,
break;
}
if (!random_sources[i].path)
{
*index = i;
return 0;
}
if (!lsh_make_pipe(output))
{
werror("spawn_source_process: Can't create pipe (errno = %i): %z\n",
werror("unix_random.c: spawn_source_process: Can't create pipe (errno = %i): %z\n",
errno, STRERROR(errno));
return 0;
}
......@@ -274,6 +328,10 @@ spawn_source_process(unsigned *index,
i++;
*index = i;
debug("unix_random.c: Trying source %z %z\n",
state->source->path,
state->source->arg ? state->source->arg : "");
pid = fork();
switch(pid)
......@@ -282,51 +340,252 @@ spawn_source_process(unsigned *index,
/* Parent */
close (output[1]);
state->fd = output[0];
io_set_close_on_exec(self->fd);
state->amount = 0;
state->pid = pid;
io_set_close_on_exec(state->fd);
io_set_nonblocking(state->fd);
state->length = 0;
return 1;
case -1:
/* Error */
close(output[0]);
close(output[1]);
return 0;
case 0:
/* Child */
if (dup2(output[1], STDOUT_FILENO))
if (dup2(output[1], STDOUT_FILENO) < 0)
{
werror("unix_random.c: spawn_source_process: dup2 for stdout failed (errno = %i): %z\n",
errno, STRERROR(errno));
_exit(EXIT_FAILURE);
}
/* Ignore stderr. */
if (dup2(null, STDERR_FILENO) < 0)
{
werror("spawn_source_process: dup2 for stdout failed (errno = %i): %z\n",
werror("unix_random.c: spawn_source_process: dup2 for stderr failed (errno = %i): %z\n",
errno, STRERROR(errno));
_exit(EXIT_FAILURE);
}
close (output[0]);
close (output[1]);
/* Works also if state->source.arg == NULL */
execl(state->source.path, state->source.arg, NULL);
/* Works also if state->source->arg == NULL */
execl(state->source->path, state->source->path, state->source->arg, NULL);
werror("spawn_source_process: execl '%z' failed (errno = %i): %z\n",
state->source.path, errno, STRERROR(errno));
state->source->path, errno, STRERROR(errno));
_exit(EXIT_FAILURE);
}
}
static unsigned
use_dev_random(struct hash_instance *hash)
{
#define DEVRANDOM_SIZE 40
UINT8 buffer[DEVRANDOM_SIZE];
unsigned count = 0;
int res;
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0)
return 0;
do
{ res = read(fd, buffer, DEVRANDOM_SIZE); }
while ( (res < 0) && (errno == EINTR));
if (res < 0)
werror("unix_random.c: Reading from /dev/urandom failed (errno = %i): %z\n",
errno, STRERROR(errno));
else if (res > 0)
{
HASH_UPDATE(hash, res, buffer);
/* Count 4 bits of entropy for each byte. */
count = res * 4;
}
else
werror("unix_random.c: No data available on /dev/urandom!\n");
close(fd);
return count;
}
static unsigned
use_procfs(struct hash_instance *hash)
{
#if 0
/* Peter Gutmann's code uses the following sources. I'm not sure
* what quality to assign to them. */
static const char *proc_sources[] =
{
"/proc/interrupts", "/proc/loadavg", "/proc/locks",
"/proc/meminfo", "/proc/stat", "/proc/net/tcp", "/proc/net/udp",
"/proc/net/dev", "/proc/net/ipx", NULL
};
#endif
/* Not implemented. */
return 0;
}
/* Spawn this number of processes. */
#define UNIX_RANDOM_POLL_PROCESSES 10
#define UNIX_RANDOM_THRESHOLD 200
static void
background_poll(struct unix_random_poll_result *result)
static unsigned
background_poll(struct hash_instance *hash)
{
struct unix_random_source_state state[UNIX_RANDOM_POLL_PROCESSES];
unsigned count = 0;
unsigned running = 0;
unsigned i = 0;
unsigned i;
unsigned index = 0;
int null;
for (running = 0; running<UNIX_RANDOM_POLL_PROCESSES; running++)
trace("unix_random.c: background_poll\n");
null = open("/dev/null", O_WRONLY);
if (null < 0)
{
werror("unix_random.c: background_poll: Failed to open /dev/null (errno = %i): %z\n",
errno, STRERROR(errno));
return count;
}
count += use_dev_random(hash);
count += use_procfs(hash);
for (i = 0; i < UNIX_RANDOM_POLL_PROCESSES; i++)
state[i].fd = -1;
for (running = 0; running<UNIX_RANDOM_POLL_PROCESSES; running++)
{
if (!spawn_source_process(&index, state + running, null))
break;
}
while (running)
{
struct pollfd fds[UNIX_RANDOM_POLL_PROCESSES];
unsigned nfds;
unsigned i;
for (i = 0, nfds = 0; i < UNIX_RANDOM_POLL_PROCESSES; i++)
if (state[i].fd > 0)
{
fds[nfds].fd = state[i].fd;
fds[nfds].events = MY_POLLIN;
nfds++;
}
assert(nfds == running);
debug("unix_random.c: calling poll, nfds = %i\n", nfds);
if (poll(fds, nfds, -1) < 0)
{
werror("unix_random.c: background_poll poll() failed (errno = %i): %z\n",
errno, STRERROR(errno));
return count;
}
debug("unix_random.c: returned from poll.\n");
for (i = 0; i<nfds; i++)
{
debug("background_poll: poll for fd %i: events = 0x%xi, revents = 0x%xi.\n",
fds[i].fd, fds[i].events, fds[i].revents);
/* Linux sets POLLHUP on EOF */
#ifndef POLLHUP
#define POLLHUP 0
#endif
if (fds[i].revents & (MY_POLLIN | POLLHUP))
{
#define BUFSIZE 1024
UINT8 buffer[BUFSIZE];
int res;
unsigned j;
for (j = 0; j<UNIX_RANDOM_POLL_PROCESSES; j++)
if (fds[i].fd == state[j].fd)
break;
assert(j < UNIX_RANDOM_POLL_PROCESSES);
debug("background_poll: reading from source %z\n",
state[j].source->path);
res = read(fds[i].fd, buffer, BUFSIZE);
#undef BUFSIZE
if (res < 0)
{
if (errno != EINTR)
{
werror("unix_random.c: background_poll read() failed (errno = %i): %z\n",
errno, STRERROR(errno));
return count;
}
}
else if (res > 0)
{
HASH_UPDATE(hash, res, buffer);
state[j].length += res;
}
else
{ /* EOF */
int status;
unsigned entropy;
close(fds[i].fd);
state[j].fd = -1;
/* Estimate entropy */
entropy = state[j].length * state[j].source->quality / UNIX_RANDOM_SOURCE_SCALE ;
if (!entropy && state[j].source->small
&& (state[j].length > state[j].source->small))
entropy = state[j].source->quality;
debug("unix_random.c: background_poll: Got %i bytes from %z %z,\n"
" estimating %i bits of entropy.\n",
state[j].length,
state[j].source->path, state[j].source->arg ? state[j].source->arg : "",
entropy);
count += entropy;
if (waitpid(state[j].pid, &status, 0) < 0)
{
werror("unix_random.c: background_poll waitpid() failed (errno = %i): %z\n",
errno, STRERROR(errno));
return count;
}
if (WIFEXITED(status))
{
if (WEXITSTATUS(status))
werror("unix_random.c: background_poll: %z exited unsuccessfully.\n",
state[j].source->path);
}
else if (WIFSIGNALED(status))
{
werror("unix_random.c: background_poll: %z died from signal %i (%z).\n",
state[j].source->path, WTERMSIG(status), STRSIGNAL(WTERMSIG(status)));
}
if (! ((count < UNIX_RANDOM_THRESHOLD)
&& spawn_source_process(&index, state + j, null)))
running--;
}
}
}
}
return count;
}
static void
......@@ -334,22 +593,16 @@ start_background_poll(struct unix_random *self)
{
pid_t pid;
int output[2];
int null;
assert(self->status == POLL_NO_POLL);
trace("unix_random.c: start_background_poll\n");
if (!lsh_make_pipe(output))
{
werror("Failed to create pipe for background randomness poll.\n");
return;
}
null = open("/dev/null", O_RDONLY);
if (dup2(null, STDIN_FILENO) < 0)
werror("start_background_poll: dup2 for stdin failed (errno = %i): %z\n",
errno, STRERROR(errno));
close(null);
pid = fork();
switch(pid)
......@@ -359,9 +612,16 @@ start_background_poll(struct unix_random *self)
close(output[1]);
self->fd = output[0];
io_set_close_on_exec(self->fd);
if (self->reaper)
REAP(self->reaper, pid, make_unix_random_callback(self));
REAP(self->reaper, pid, make_unix_random_callback(self));
self->pid = pid;
self->status = POLL_RUNNING;
werror("unix_random.c: Started background poll. pid = %i.\n",
pid);
return;
case -1:
......@@ -374,7 +634,31 @@ start_background_poll(struct unix_random *self)
/* Child */
{
struct unix_random_poll_result result;
struct hash_instance *hash = MAKE_HASH(&sha1_algorithm);
#if 0
int hang = 1;
while (hang)
sleep(1);
#endif
int null = open("/dev/null", O_RDONLY);
if (null < 0)
{
werror("start_background_poll: Failed to open /dev/null (errno = %i): %z\n",
errno, STRERROR(errno));
_exit(EXIT_FAILURE);
}
if (dup2(null, STDIN_FILENO) < 0)
{
werror("start_background_poll: dup2 for stdin failed (errno = %i): %z\n",
errno, STRERROR(errno));
_exit(EXIT_FAILURE);
}
close(null);
close(output[0]);
io_set_close_on_exec(output[1]);
......@@ -383,13 +667,17 @@ start_background_poll(struct unix_random *self)
setuid(self->poll_uid);
if (!getuid())
_exit(1);
_exit(EXIT_FAILURE);
result.count = background_poll(hash);
background_poll(&result);
assert(hash->hash_size == sizeof(result.data));
HASH_DIGEST(hash, result.data);
if (!write_raw(output[1], sizeof(result), (UINT8 *) &result))
_exit(0);
_exit(EXIT_SUCCESS);
_exit(2);
_exit(EXIT_FAILURE);
}
}
}
......@@ -398,10 +686,10 @@ static void
wait_background_poll(struct unix_random *self)
{
int status;
pid_t child;
assert(self->state == POLL_RUNNING);
self->state = POLL_FAILED;
assert(self->status == POLL_RUNNING);
trace("unix_random.c: wait_background_poll\n");
self->status = POLL_FAILED;
if (waitpid(self->pid, &status, 0) == self->pid)
{
......@@ -409,14 +697,15 @@ wait_background_poll(struct unix_random *self)
self->status = POLL_FINISHED;
}
REAP(self->reaper, self->pid, NULL);
if (self->reaper)
REAP(self->reaper, self->pid, NULL);
}
static unsigned
finish_background_poll(struct unix_random *self, struct hash_instance *hash)
{
unsigned count;
unsigned count = 0;
switch(self->status)
{
......@@ -438,7 +727,6 @@ finish_background_poll(struct unix_random *self, struct hash_instance *hash)
}
case POLL_FAILED:
werror("Background randomness poll failed.\n");
count = 0;
break;
case POLL_NO_POLL:
......@@ -453,11 +741,32 @@ finish_background_poll(struct unix_random *self, struct hash_instance *hash)
return count;
}
static unsigned
use_seed_file(struct hash_instance *hash)
{
if (getuid())
{
/* Lock and read seed file. */
}
return 0;
}
#define HASH_OBJECT(h, x) HASH_UPDATE((h), sizeof(x), (UINT8 *) &(x))
static unsigned
do_unix_random_slow(struct random_poll *s, struct hash_instance *hash)
{
CAST(unix_random, self, s);
unsigned count;
time_t now = time(NULL);
pid_t pid = getpid();
/* Make sure we don't start with the same state, if several
* instances are initializing at about the same time. */
HASH_OBJECT(hash, now);
HASH_OBJECT(hash, pid);
if (self->status == POLL_NO_POLL)
start_background_poll(self);
......@@ -467,17 +776,17 @@ do_unix_random_slow(struct random_poll *s, struct hash_instance *hash)
count = finish_background_poll(self, hash);
/* Do this in the main process, as the background process may run as
* different user. */
count += use_seed_file(hash);
count += use_procfs(hash);
return count;
}
#define HASH_OBJECT(hash, x) HASH_UPDATE((h), sizeof(x), (UINT8 *) &(x))
static unsigned
do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
{
CAST(unix_random, self, s);
unsigned count = 0;
#if HAVE_GETRUSAGE
......@@ -516,7 +825,7 @@ do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
count++;
HASH_OBJECT(hash, now);
HASH_OBJECT(self->time_count);
HASH_OBJECT(hash, self->time_count);
self->time_count = 0;
self->previous_time = now;
......@@ -526,3 +835,38 @@ do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
return count;
}
static void do_unix_random_background(struct random_poll *s)
{
CAST(unix_random, self, s);
start_background_poll(self);
}
/* Using a NULL reaper argument is ok. It must be supplied only if the
* application is using a reaper, as that may get to our child process
* before we can waitpid it. */
struct random_poll *
make_unix_random(struct reap *reaper)
{
NEW(unix_random, self);
struct passwd *pw = getpwnam("nobody");
self->super.slow = do_unix_random_slow;
self->super.fast = do_unix_random_fast;
self->super.background = do_unix_random_background;
self->reaper = reaper;
if (pw && pw->pw_uid)
self->poll_uid = pw->pw_uid;
else
self->poll_uid = (uid_t) -1;
self->status = POLL_NO_POLL;
self->previous_time = time(NULL);
self->time_count = 0;