summaryrefslogtreecommitdiffstats
path: root/sys/dev/random/randomdev.c
diff options
context:
space:
mode:
authormarkm <markm@FreeBSD.org>2014-10-30 21:21:53 +0000
committermarkm <markm@FreeBSD.org>2014-10-30 21:21:53 +0000
commitfce6747f55fd538917f2bd60e601dc95866c16d0 (patch)
treed5dfa61a018a7d209b25f173c6ee76709037035a /sys/dev/random/randomdev.c
parentda82006ada584e84400ef56335e9df6c4e72bc6e (diff)
downloadFreeBSD-src-fce6747f55fd538917f2bd60e601dc95866c16d0.zip
FreeBSD-src-fce6747f55fd538917f2bd60e601dc95866c16d0.tar.gz
This is the much-discussed major upgrade to the random(4) device, known to you all as /dev/random.
This code has had an extensive rewrite and a good series of reviews, both by the author and other parties. This means a lot of code has been simplified. Pluggable structures for high-rate entropy generators are available, and it is most definitely not the case that /dev/random can be driven by only a hardware souce any more. This has been designed out of the device. Hardware sources are stirred into the CSPRNG (Yarrow, Fortuna) like any other entropy source. Pluggable modules may be written by third parties for additional sources. The harvesting structures and consequently the locking have been simplified. Entropy harvesting is done in a more general way (the documentation for this will follow). There is some GREAT entropy to be had in the UMA allocator, but it is disabled for now as messing with that is likely to annoy many people. The venerable (but effective) Yarrow algorithm, which is no longer supported by its authors now has an alternative, Fortuna. For now, Yarrow is retained as the default algorithm, but this may be changed using a kernel option. It is intended to make Fortuna the default algorithm for 11.0. Interested parties are encouraged to read ISBN 978-0-470-47424-2 "Cryptography Engineering" By Ferguson, Schneier and Kohno for Fortuna's gory details. Heck, read it anyway. Many thanks to Arthur Mesh who did early grunt work, and who got caught in the crossfire rather more than he deserved to. My thanks also to folks who helped me thresh this out on whiteboards and in the odd "Hallway track", or otherwise. My Nomex pants are on. Let the feedback commence! Reviewed by: trasz,des(partial),imp(partial?),rwatson(partial?) Approved by: so(des)
Diffstat (limited to 'sys/dev/random/randomdev.c')
-rw-r--r--sys/dev/random/randomdev.c271
1 files changed, 145 insertions, 126 deletions
diff --git a/sys/dev/random/randomdev.c b/sys/dev/random/randomdev.c
index b76cb83..bc41d51 100644
--- a/sys/dev/random/randomdev.c
+++ b/sys/dev/random/randomdev.c
@@ -26,9 +26,28 @@
*
*/
+/*
+ * NOTE NOTE NOTE
+ *
+ * This file is compiled into the kernel unconditionally. Any random(4)
+ * infrastructure that needs to be in the kernel by default goes here!
+ *
+ * Except ...
+ *
+ * The adaptor code all goes into random_adaptor.c, which is also compiled
+ * the kernel by default. The module in that file is initialised before
+ * this one.
+ *
+ * Other modules must be initialised after the above two, and are
+ * software random processors which plug into random_adaptor.c.
+ *
+ */
+
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_random.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -38,196 +57,196 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/lock.h>
-#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/mutex.h>
-#include <sys/poll.h>
-#include <sys/priv.h>
+#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/random.h>
-#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/unistd.h>
-#include <machine/bus.h>
-#include <machine/cpu.h>
-
#include <dev/random/randomdev.h>
-#include <dev/random/randomdev_soft.h>
#include <dev/random/random_adaptors.h>
#include <dev/random/random_harvestq.h>
-#include <dev/random/live_entropy_sources.h>
#define RANDOM_MINOR 0
-static d_read_t random_read;
-static d_write_t random_write;
-static d_ioctl_t random_ioctl;
-static d_poll_t random_poll;
+static d_ioctl_t randomdev_ioctl;
static struct cdevsw random_cdevsw = {
- .d_version = D_VERSION,
- .d_read = random_read,
- .d_write = random_write,
- .d_ioctl = random_ioctl,
- .d_poll = random_poll,
.d_name = "random",
+ .d_version = D_VERSION,
+ .d_read = random_adaptor_read,
+ .d_write = random_adaptor_write,
+ .d_poll = random_adaptor_poll,
+ .d_ioctl = randomdev_ioctl,
};
/* For use with make_dev(9)/destroy_dev(9). */
static struct cdev *random_dev;
+/* Set up the sysctl root node for the entropy device */
+SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator");
+
+MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures");
+
/* ARGSUSED */
static int
-random_read(struct cdev *dev __unused, struct uio *uio, int flag)
+randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
+ int flags __unused, struct thread *td __unused)
{
- int c, error = 0;
- void *random_buf;
-
- /* Blocking logic */
- if (!random_adaptor->seeded)
- error = (*random_adaptor->block)(flag);
-
- /* The actual read */
- if (!error) {
-
- random_buf = (void *)malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
+ int error = 0;
- while (uio->uio_resid > 0 && !error) {
- c = MIN(uio->uio_resid, PAGE_SIZE);
- c = (*random_adaptor->read)(random_buf, c);
- error = uiomove(random_buf, c, uio);
- }
- /* Finished reading; let the source know so it can do some
- * optional housekeeping */
- (*random_adaptor->read)(NULL, 0);
+ switch (cmd) {
+ /* Really handled in upper layer */
+ case FIOASYNC:
+ case FIONBIO:
+ break;
- free(random_buf, M_ENTROPY);
+ default:
+ error = ENOTTY;
}
return (error);
}
-/* ARGSUSED */
-static int
-random_write(struct cdev *dev __unused, struct uio *uio, int flag __unused)
+/* Helper routine to enable kproc_exit() to work while the module is
+ * being (or has been) unloaded.
+ * This routine is in this file because it is always linked into the kernel,
+ * and will thus never be unloaded. This is critical for unloadable modules
+ * that have threads.
+ */
+void
+randomdev_set_wakeup_exit(void *control)
{
- /* We used to allow this to insert userland entropy.
- * We don't any more because (1) this so-called entropy
- * is usually lousy and (b) its vaguely possible to
- * mess with entropy harvesting by overdoing a write.
- * Now we just ignore input like /dev/null does.
- */
- uio->uio_resid = 0;
-
- return (0);
+ wakeup(control);
+ kproc_exit(0);
+ /* NOTREACHED */
}
/* ARGSUSED */
static int
-random_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
- int flags __unused, struct thread *td __unused)
+randomdev_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
- switch (cmd) {
- /* Really handled in upper layer */
- case FIOASYNC:
- case FIONBIO:
+ switch (type) {
+ case MOD_LOAD:
+ printf("random: entropy device infrastructure driver\n");
+ random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
+ RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0644, "random");
+ make_dev_alias(random_dev, "urandom"); /* compatibility */
+ random_adaptors_init();
break;
+
+ case MOD_UNLOAD:
+ random_adaptors_deinit();
+ destroy_dev(random_dev);
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
default:
- error = ENOTTY;
+ error = EOPNOTSUPP;
+ break;
+
}
+
return (error);
}
-/* ARGSUSED */
-static int
-random_poll(struct cdev *dev __unused, int events, struct thread *td)
-{
- int revents = 0;
+#define EARLY_2_DEV_MODULE(name, evh, arg) \
+static moduledata_t name##_mod = { \
+ #name, \
+ evh, \
+ arg \
+}; \
+DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND)
- if (events & (POLLIN | POLLRDNORM)) {
- if (random_adaptor->seeded)
- revents = events & (POLLIN | POLLRDNORM);
- else
- revents = (*random_adaptor->poll)(events, td);
- }
- return (revents);
-}
+EARLY_2_DEV_MODULE(randomdev, randomdev_modevent, NULL);
+MODULE_VERSION(randomdev, 1);
+
+/* ================
+ * Harvesting stubs
+ * ================
+ */
+/* Internal stub/fake routine for when no entropy processor is loaded.
+ * If the entropy device is not loaded, don't act on harvesting calls
+ * and just return.
+ */
+/* ARGSUSED */
static void
-random_initialize(void *p, struct random_adaptor *s)
+random_harvest_phony(const void *entropy __unused, u_int count __unused,
+ u_int bits __unused, enum random_entropy_source origin __unused)
{
- static int random_inited = 0;
-
- if (random_inited) {
- printf("random: <%s> already initialized\n",
- random_adaptor->ident);
- return;
- }
+}
- random_adaptor = s;
+/* Hold the address of the routine which is actually called */
+static void (*reap_func)(const void *, u_int, u_int, enum random_entropy_source) = random_harvest_phony;
- (s->init)();
+/* Initialise the harvester when/if it is loaded */
+void
+randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source))
+{
- printf("random: <%s> initialized\n", s->ident);
+ reap_func = reaper;
+}
- /* Use an appropriately evil mode for those who are concerned
- * with daemons */
- random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
- RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random");
- make_dev_alias(random_dev, "urandom"); /* compatibility */
+/* Deinitialise the harvester when/if it is unloaded */
+void
+randomdev_deinit_harvester(void)
+{
- /* mark random(4) as initialized, to avoid being called again */
- random_inited = 1;
+ reap_func = random_harvest_phony;
}
-/* ARGSUSED */
-static int
-random_modevent(module_t mod __unused, int type, void *data __unused)
+/* Entropy harvesting routine.
+ * Implemented as in indirect call to allow non-inclusion of
+ * the entropy device.
+ */
+void
+random_harvest(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin)
{
- static eventhandler_tag attach_tag = NULL;
- int error = 0;
-
- switch (type) {
- case MOD_LOAD:
- random_adaptor_choose(&random_adaptor);
- if (random_adaptor == NULL) {
- printf("random: No random adaptor attached, "
- "postponing initialization\n");
- attach_tag = EVENTHANDLER_REGISTER(random_adaptor_attach,
- random_initialize, NULL, EVENTHANDLER_PRI_ANY);
- } else
- random_initialize(NULL, random_adaptor);
+ (*reap_func)(entropy, count, bits, origin);
+}
- break;
+/* ================================
+ * Internal reading stubs and fakes
+ * ================================
+ */
- case MOD_UNLOAD:
- if (random_adaptor != NULL) {
- (*random_adaptor->deinit)();
- destroy_dev(random_dev);
- }
- /* Unregister the event handler */
- if (attach_tag != NULL)
- EVENTHANDLER_DEREGISTER(random_adaptor_attach,
- attach_tag);
+/* Hold the address of the routine which is actually called */
+static u_int (*read_func)(uint8_t *, u_int) = dummy_random_read_phony;
- break;
+/* Initialise the reader when/if it is loaded */
+void
+randomdev_init_reader(u_int (*reader)(uint8_t *, u_int))
+{
- case MOD_SHUTDOWN:
- break;
+ read_func = reader;
+}
- default:
- error = EOPNOTSUPP;
- break;
+/* Deinitialise the reader when/if it is unloaded */
+void
+randomdev_deinit_reader(void)
+{
- }
- return (error);
+ read_func = dummy_random_read_phony;
}
-DEV_MODULE(random, random_modevent, NULL);
-MODULE_VERSION(random, 1);
+/* Kernel API version of read_random().
+ * Implemented as in indirect call to allow non-inclusion of
+ * the entropy device.
+ */
+int
+read_random(void *buf, int count)
+{
+
+ return ((int)(*read_func)(buf, (u_int)count));
+}
OpenPOWER on IntegriCloud