601e8bcdf4
randomdev_init_reader to change read_random over to the newly installed adaptor was missed. This means both read_random and arc4random (seeded from read_random) were not returning very random data. This also effects userland arc4random as it is seeded from kernel arc4random. The random devices are uneffected and have returned good randomness since the change. All keys generated with a kernel of r273872 must be regenerated with a kernel with this patch. Keys generated may be predictable. Remove the warning as log is too early to print anything, and it would always get printed due to early use of arc4random... Reviewed by: delphij, markm Approved by: so (delphij)
484 lines
13 KiB
C
484 lines
13 KiB
C
/*-
|
|
* Copyright (c) 2013 Mark R V Murray
|
|
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
|
|
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer
|
|
* in this position and unchanged.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_random.h"
|
|
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/kthread.h>
|
|
#include <sys/libkern.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/random.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/selinfo.h>
|
|
#include <sys/sx.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/unistd.h>
|
|
|
|
#include <dev/random/randomdev.h>
|
|
#include <dev/random/random_adaptors.h>
|
|
#include <dev/random/live_entropy_sources.h>
|
|
|
|
/* The random_adaptors_lock protects random_adaptors_list and friends and random_adaptor.
|
|
* We need a sleepable lock for uiomove/block/poll/sbuf/sysctl.
|
|
*/
|
|
static struct sx random_adaptors_lock;
|
|
LIST_HEAD(adaptors_head, random_adaptors);
|
|
static struct adaptors_head random_adaptors_list = LIST_HEAD_INITIALIZER(random_adaptors_list);
|
|
static struct random_adaptor *random_adaptor = NULL; /* Currently active adaptor */
|
|
/* End of data items requiring random_adaptors_lock protection */
|
|
|
|
/* The random_readrate_mtx mutex protects the read-rate estimator.
|
|
*/
|
|
static struct mtx random_read_rate_mtx;
|
|
static int random_adaptor_read_rate_cache;
|
|
/* End of data items requiring random_readrate_mtx mutex protection */
|
|
|
|
static struct selinfo rsel;
|
|
|
|
/* Utility routine to change active adaptor when the random_adaptors_list
|
|
* gets modified.
|
|
*
|
|
* Walk a list of registered random(4) adaptors and pick either a requested
|
|
* one or the highest priority one, whichever comes first. Panic on failure
|
|
* as the fallback must always be the "dummy" adaptor.
|
|
*/
|
|
static void
|
|
random_adaptor_choose(void)
|
|
{
|
|
char rngs[128], *token, *cp;
|
|
struct random_adaptors *rra, *rrai;
|
|
struct random_adaptor *random_adaptor_previous;
|
|
int primax;
|
|
|
|
/* We are going to be messing with random_adaptor.
|
|
* Exclusive lock is mandatory.
|
|
*/
|
|
sx_assert(&random_adaptors_lock, SA_XLOCKED);
|
|
|
|
random_adaptor_previous = random_adaptor;
|
|
|
|
random_adaptor = NULL;
|
|
if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) {
|
|
cp = rngs;
|
|
|
|
/* XXX: FIX!! (DES):
|
|
* - fetch tunable once, at boot
|
|
* - make sysctl r/w
|
|
* - when fetching tunable or processing a sysctl
|
|
* write, parse into list of strings so we don't
|
|
* have to do it here again and again
|
|
* - sysctl read should return a reconstructed string
|
|
*/
|
|
while ((token = strsep(&cp, ",")) != NULL) {
|
|
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
|
if (strcmp(rra->rra_name, token) == 0) {
|
|
random_adaptor = rra->rra_ra;
|
|
break;
|
|
}
|
|
if (random_adaptor != NULL) {
|
|
printf("random: selecting requested adaptor <%s>\n",
|
|
random_adaptor->ra_ident);
|
|
break;
|
|
}
|
|
else
|
|
printf("random: requested adaptor <%s> not available\n",
|
|
token);
|
|
}
|
|
}
|
|
|
|
primax = 0;
|
|
if (random_adaptor == NULL) {
|
|
/*
|
|
* Fall back to the highest priority item on the available
|
|
* RNG list.
|
|
*/
|
|
LIST_FOREACH(rrai, &random_adaptors_list, rra_entries) {
|
|
if (rrai->rra_ra->ra_priority >= primax) {
|
|
random_adaptor = rrai->rra_ra;
|
|
primax = rrai->rra_ra->ra_priority;
|
|
}
|
|
}
|
|
if (random_adaptor != NULL)
|
|
printf("random: selecting highest priority adaptor <%s>\n",
|
|
random_adaptor->ra_ident);
|
|
}
|
|
|
|
KASSERT(random_adaptor != NULL, ("adaptor not found"));
|
|
|
|
/* If we are changing adaptors, deinit the old and init the new. */
|
|
if (random_adaptor != random_adaptor_previous) {
|
|
#ifdef RANDOM_DEBUG
|
|
printf("random: %s - changing from %s to %s\n", __func__,
|
|
(random_adaptor_previous == NULL ? "NULL" : random_adaptor_previous->ra_ident),
|
|
random_adaptor->ra_ident);
|
|
#endif
|
|
if (random_adaptor_previous != NULL) {
|
|
randomdev_deinit_reader();
|
|
(random_adaptor_previous->ra_deinit)();
|
|
}
|
|
(random_adaptor->ra_init)();
|
|
}
|
|
|
|
randomdev_init_reader(random_adaptor->ra_read);
|
|
}
|
|
|
|
|
|
/* XXX: FIX!! Make sure we are not inserting a duplicate */
|
|
void
|
|
random_adaptor_register(const char *name, struct random_adaptor *ra)
|
|
{
|
|
struct random_adaptors *rra;
|
|
|
|
KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__));
|
|
|
|
rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK);
|
|
rra->rra_name = name;
|
|
rra->rra_ra = ra;
|
|
|
|
sx_xlock(&random_adaptors_lock);
|
|
LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries);
|
|
random_adaptor_choose();
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
sx_xunlock(&random_adaptors_lock);
|
|
}
|
|
|
|
void
|
|
random_adaptor_deregister(const char *name)
|
|
{
|
|
struct random_adaptors *rra;
|
|
|
|
KASSERT(name != NULL, ("invalid input to %s", __func__));
|
|
|
|
sx_xlock(&random_adaptors_lock);
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
|
if (strcmp(rra->rra_name, name) == 0) {
|
|
LIST_REMOVE(rra, rra_entries);
|
|
break;
|
|
}
|
|
random_adaptor_choose();
|
|
sx_xunlock(&random_adaptors_lock);
|
|
|
|
free(rra, M_ENTROPY);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags)
|
|
{
|
|
void *random_buf;
|
|
int c, error;
|
|
ssize_t nbytes;
|
|
|
|
#ifdef RANDOM_DEBUG_VERBOSE
|
|
printf("random: %s %ld\n", __func__, uio->uio_resid);
|
|
#endif
|
|
|
|
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
|
|
/* Let the entropy source do any pre-read setup. */
|
|
(random_adaptor->ra_read)(NULL, 0);
|
|
|
|
/* (Un)Blocking logic */
|
|
error = 0;
|
|
while (!random_adaptor->ra_seeded() && error == 0) {
|
|
if (flags & O_NONBLOCK) {
|
|
error = EWOULDBLOCK;
|
|
break;
|
|
}
|
|
|
|
/* Sleep instead of going into a spin-frenzy */
|
|
error = sx_sleep(&random_adaptor, &random_adaptors_lock,
|
|
PUSER | PCATCH, "randrd", hz/10);
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s",
|
|
__func__));
|
|
|
|
/* keep tapping away at the pre-read until we seed/unblock. */
|
|
(random_adaptor->ra_read)(NULL, 0);
|
|
}
|
|
|
|
mtx_lock(&random_read_rate_mtx);
|
|
|
|
/* The read-rate stuff is a rough indication of the instantaneous read rate,
|
|
* used to increase the use of 'live' entropy sources when lots of reads are done.
|
|
*/
|
|
nbytes = (uio->uio_resid + 32 - 1)/32; /* Round up to units of 32 */
|
|
random_adaptor_read_rate_cache += nbytes*32;
|
|
random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32);
|
|
|
|
mtx_unlock(&random_read_rate_mtx);
|
|
|
|
if (error == 0) {
|
|
nbytes = uio->uio_resid;
|
|
|
|
/* The actual read */
|
|
while (uio->uio_resid && !error) {
|
|
c = MIN(uio->uio_resid, PAGE_SIZE);
|
|
(random_adaptor->ra_read)(random_buf, c);
|
|
error = uiomove(random_buf, c, uio);
|
|
}
|
|
|
|
/* Let the entropy source do any post-read cleanup. */
|
|
(random_adaptor->ra_read)(NULL, 1);
|
|
|
|
if (nbytes != uio->uio_resid && (error == ERESTART ||
|
|
error == EINTR) )
|
|
error = 0; /* Return partial read, not error. */
|
|
|
|
}
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
free(random_buf, M_ENTROPY);
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
random_adaptor_read_rate(void)
|
|
{
|
|
int ret;
|
|
|
|
mtx_lock(&random_read_rate_mtx);
|
|
|
|
ret = random_adaptor_read_rate_cache;
|
|
random_adaptor_read_rate_cache = 1;
|
|
|
|
mtx_unlock(&random_read_rate_mtx);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
|
|
{
|
|
int c, error = 0;
|
|
void *random_buf;
|
|
ssize_t nbytes;
|
|
|
|
#ifdef RANDOM_DEBUG
|
|
printf("random: %s %zd\n", __func__, uio->uio_resid);
|
|
#endif
|
|
|
|
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
|
|
nbytes = uio->uio_resid;
|
|
while (uio->uio_resid > 0 && error == 0) {
|
|
c = MIN(uio->uio_resid, PAGE_SIZE);
|
|
error = uiomove(random_buf, c, uio);
|
|
if (error)
|
|
break;
|
|
(random_adaptor->ra_write)(random_buf, c);
|
|
|
|
/* Introduce an annoying delay to stop swamping */
|
|
error = sx_sleep(&random_adaptor, &random_adaptors_lock,
|
|
PUSER | PCATCH, "randwr", hz/10);
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s",
|
|
__func__));
|
|
}
|
|
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
if (nbytes != uio->uio_resid && (error == ERESTART ||
|
|
error == EINTR) )
|
|
error = 0; /* Partial write, not error. */
|
|
|
|
free(random_buf, M_ENTROPY);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
|
|
{
|
|
|
|
#ifdef RANDOM_DEBUG
|
|
printf("random: %s\n", __func__);
|
|
#endif
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
|
|
if (events & (POLLIN | POLLRDNORM)) {
|
|
if (random_adaptor->ra_seeded())
|
|
events &= (POLLIN | POLLRDNORM);
|
|
else
|
|
selrecord(td, &rsel);
|
|
}
|
|
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
return (events);
|
|
}
|
|
|
|
/* This will be called by the entropy processor when it seeds itself and becomes secure */
|
|
void
|
|
random_adaptor_unblock(void)
|
|
{
|
|
|
|
selwakeuppri(&rsel, PUSER);
|
|
wakeup(&random_adaptor);
|
|
printf("random: unblocking device.\n");
|
|
|
|
/* Do arc4random(9) a favour while we are about it. */
|
|
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE);
|
|
}
|
|
|
|
static int
|
|
random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct random_adaptors *rra;
|
|
struct sbuf sbuf;
|
|
int error, count;
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
|
|
count = 0;
|
|
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
|
sbuf_printf(&sbuf, "%s%s(%d)",
|
|
(count++ ? "," : ""), rra->rra_name, rra->rra_ra->ra_priority);
|
|
|
|
error = sbuf_finish(&sbuf);
|
|
sbuf_delete(&sbuf);
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS)
|
|
{
|
|
struct random_adaptors *rra;
|
|
struct sbuf sbuf;
|
|
int error;
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
|
|
sbuf_new_for_sysctl(&sbuf, NULL, 16, req);
|
|
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
|
if (rra->rra_ra == random_adaptor) {
|
|
sbuf_cat(&sbuf, rra->rra_name);
|
|
break;
|
|
}
|
|
error = sbuf_finish(&sbuf);
|
|
sbuf_delete(&sbuf);
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
random_adaptors_init(void)
|
|
{
|
|
|
|
#ifdef RANDOM_DEBUG
|
|
printf("random: %s\n", __func__);
|
|
#endif
|
|
|
|
SYSCTL_PROC(_kern_random, OID_AUTO, adaptors,
|
|
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
|
NULL, 0, random_sysctl_adaptors_handler, "A",
|
|
"Random Number Generator adaptors");
|
|
|
|
SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor,
|
|
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
|
NULL, 0, random_sysctl_active_adaptor_handler, "A",
|
|
"Active Random Number Generator Adaptor");
|
|
|
|
sx_init(&random_adaptors_lock, "random_adaptors");
|
|
|
|
mtx_init(&random_read_rate_mtx, "read rate mutex", NULL, MTX_DEF);
|
|
|
|
/* The dummy adaptor is not a module by itself, but part of the
|
|
* randomdev module.
|
|
*/
|
|
random_adaptor_register("dummy", &randomdev_dummy);
|
|
|
|
live_entropy_sources_init();
|
|
}
|
|
|
|
void
|
|
random_adaptors_deinit(void)
|
|
{
|
|
|
|
#ifdef RANDOM_DEBUG
|
|
printf("random: %s\n", __func__);
|
|
#endif
|
|
|
|
live_entropy_sources_deinit();
|
|
|
|
/* Don't do this! Panic will surely follow! */
|
|
/* random_adaptor_deregister("dummy"); */
|
|
|
|
mtx_destroy(&random_read_rate_mtx);
|
|
|
|
sx_destroy(&random_adaptors_lock);
|
|
}
|
|
|
|
/*
|
|
* Reseed the active adaptor shortly before starting init(8).
|
|
*/
|
|
/* ARGSUSED */
|
|
static void
|
|
random_adaptors_seed(void *unused __unused)
|
|
{
|
|
|
|
sx_slock(&random_adaptors_lock);
|
|
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
|
|
|
random_adaptor->ra_reseed();
|
|
sx_sunlock(&random_adaptors_lock);
|
|
|
|
arc4rand(NULL, 0, 1);
|
|
}
|
|
SYSINIT(random_seed, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST,
|
|
random_adaptors_seed, NULL);
|