summaryrefslogtreecommitdiffstats
path: root/sys/dev/random/random_adaptors.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/random/random_adaptors.c')
-rw-r--r--sys/dev/random/random_adaptors.c228
1 files changed, 219 insertions, 9 deletions
diff --git a/sys/dev/random/random_adaptors.c b/sys/dev/random/random_adaptors.c
index c187bdb..b017993 100644
--- a/sys/dev/random/random_adaptors.c
+++ b/sys/dev/random/random_adaptors.c
@@ -1,6 +1,7 @@
/*-
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
+ * Copyright (c) 2004 Mark R V Murray
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,16 +31,20 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/systm.h>
+#include <sys/kthread.h>
#include <sys/lock.h>
+#include <sys/random.h>
#include <sys/selinfo.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/libkern.h>
+#include <sys/unistd.h>
-#include <dev/random/random_adaptors.h>
#include <dev/random/randomdev.h>
+#include <dev/random/randomdev_soft.h>
+#include <dev/random/random_adaptors.h>
LIST_HEAD(adaptors_head, random_adaptors);
static struct adaptors_head adaptors = LIST_HEAD_INITIALIZER(adaptors);
@@ -50,6 +55,11 @@ static struct sysctl_ctx_list random_clist;
MALLOC_DEFINE(M_RANDOM_ADAPTORS, "random_adaptors", "Random adaptors buffers");
+struct entropy_thread_ctx {
+ struct random_adaptor *adaptor;
+ int *control;
+};
+
int
random_adaptor_register(const char *name, struct random_adaptor *rsp)
{
@@ -57,7 +67,8 @@ random_adaptor_register(const char *name, struct random_adaptor *rsp)
KASSERT(name != NULL && rsp != NULL, ("invalid input to %s", __func__));
- rpp = malloc(sizeof(struct random_adaptors), M_RANDOM_ADAPTORS, M_WAITOK);
+ rpp = malloc(sizeof(struct random_adaptors), M_RANDOM_ADAPTORS,
+ M_WAITOK);
rpp->name = name;
rpp->rsp = rsp;
@@ -87,6 +98,159 @@ random_adaptor_get(const char *name)
return (rsp);
}
+/*
+ * In the past, the logic of the random_adaptor selection was inverted, such
+ * that hardware RNGs would be chosen unless disabled. This routine is here to
+ * preserve that functionality to avoid folks losing their hardware RNGs by
+ * upgrading to newer kernel.
+ */
+static void
+random_adaptor_choose_legacy(struct random_adaptor **adaptor)
+{
+ struct random_adaptor *tmp;
+ int enable;
+
+ /* Then go looking for hardware */
+ enable = 1;
+ TUNABLE_INT_FETCH("hw.nehemiah_rng_enable", &enable);
+ if (enable && (tmp = random_adaptor_get("nehemiah")))
+ *adaptor = tmp;
+
+ enable = 1;
+ TUNABLE_INT_FETCH("hw.ivy_rng_enable", &enable);
+ if (enable && (tmp = random_adaptor_get("rdrand")))
+ *adaptor = tmp;
+}
+
+/*
+ * Walk a list of registered random(4) adaptors and pick the last non-selected
+ * one.
+ *
+ * If none are selected, use yarrow if available.
+ */
+void
+random_adaptor_choose(struct random_adaptor **adaptor)
+{
+ char rngs[128], *token, *cp;
+ struct random_adaptors *rpp;
+
+ KASSERT(adaptor != NULL, ("pre-conditions failed"));
+
+ *adaptor = NULL;
+
+ random_adaptor_choose_legacy(adaptor);
+
+ if (*adaptor != NULL)
+ return;
+
+ if (TUNABLE_STR_FETCH("rngs_want", rngs, sizeof(rngs))) {
+ cp = rngs;
+
+ while ((token = strsep(&cp, ",")) != NULL) {
+ if ((*adaptor = random_adaptor_get(token)) != NULL)
+ break;
+ else if (bootverbose)
+ printf(
+ "%s random adaptor is not available, skipping\n",
+ token);
+ }
+ }
+
+ if (*adaptor == NULL) {
+ /*
+ * Either no RNGs are prefered via rngs_want tunable, or
+ * no prefered RNGs are registered.
+ * Fallback to Yarrow.
+ */
+ *adaptor = random_adaptor_get("yarrow");
+
+ if (*adaptor == NULL) {
+ /*
+ * Yarrow doesn't seem to be available.
+ * Fallback to the first thing that's on the list of
+ * available RNGs.
+ */
+ sx_slock(&adaptors_lock);
+
+ rpp = LIST_FIRST(&adaptors);
+ if (rpp != NULL)
+ *adaptor = rpp->rsp;
+
+ sx_sunlock(&adaptors_lock);
+ }
+
+ if (bootverbose && *adaptor)
+ printf("Falling back to <%s> random adaptor",
+ (*adaptor)->ident);
+ }
+}
+
+static void
+random_proc(void *arg)
+{
+ struct entropy_thread_ctx *ctx;
+ u_char randomness[HARVESTSIZE];
+ int i;
+
+ ctx = (struct entropy_thread_ctx *)arg;
+
+ /* Sanity check. */
+ if (ctx->adaptor == NULL || ctx->adaptor->read == NULL)
+ return;
+
+ for (; *ctx->control == 0;) {
+ i = ctx->adaptor->read(randomness, sizeof(randomness));
+
+ if (i > 0)
+ /* Be very conservative with entropy estimation here. */
+ random_harvest(randomness, i, 0, 0, RANDOM_PURE);
+
+ /* Wake up every 10 secs. */
+ tsleep_sbt(ctx->adaptor, PWAIT | PCATCH, "-", SBT_1M / 6, 0, 0);
+ }
+
+ printf("<%s> entropy source is exiting\n", ctx->adaptor->ident);
+ free(ctx, M_RANDOM_ADAPTORS);
+ kproc_exit(0);
+}
+
+/*
+ * Use RNG's output as an entropy source for another RNG. i.e.:
+ * +--------+ +--------+
+ * | Intel | | Yarrow |
+ * | RDRAND +--------->| |
+ * +--------+ +--------+
+ * Very useful for seeding software RNGs with output of
+ * Hardware RNGs like Intel's RdRand and VIA's Padlock.
+ *
+ * Returns a handle to the newly created kernel process.
+ */
+void *
+random_adaptor_use_as_entropy(const char *id, struct random_adaptor *adaptor,
+ int *control)
+{
+ int error;
+ struct proc *random_chain_proc;
+ struct entropy_thread_ctx *ctx;
+
+ KASSERT(adaptor != NULL, ("can't obtain randomness"));
+ KASSERT(control != NULL, ("can't control entropy process"));
+
+ ctx = malloc(sizeof(struct entropy_thread_ctx), M_RANDOM_ADAPTORS,
+ M_WAITOK);
+
+ ctx->control = control;
+ ctx->adaptor = adaptor;
+
+ /* Start the thread */
+ error = kproc_create(random_proc, ctx, &random_chain_proc, RFHIGHPID,
+ 0, "%s_entropy", id);
+ if (error != 0)
+ panic("Cannot create rng chaining thread");
+
+ return random_chain_proc;
+}
+
static void
random_adaptors_deinit(void *unused)
{
@@ -99,18 +263,28 @@ static int
random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
{
struct random_adaptors *rpp;
- int error;
+ int error, count;
- error = 0;
+ count = error = 0;
sx_slock(&adaptors_lock);
- if (LIST_EMPTY(&adaptors))
- error = SYSCTL_OUT(req, "", strlen(""));
+ if (LIST_EMPTY(&adaptors)) {
+ error = SYSCTL_OUT(req, "", 0);
+ } else {
+
+ LIST_FOREACH(rpp, &adaptors, entries) {
+
+ error = SYSCTL_OUT(req, ",", count++ ? 1 : 0);
+
+ if (error)
+ break;
+
+ error = SYSCTL_OUT(req, rpp->name, strlen(rpp->name));
- LIST_FOREACH(rpp, &adaptors, entries) {
- if (0 != SYSCTL_OUT(req, rpp->name, strlen(rpp->name)))
- break;
+ if (error)
+ break;
+ }
}
sx_sunlock(&adaptors_lock);
@@ -118,6 +292,37 @@ random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
return (error);
}
+static int
+random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS)
+{
+ struct random_adaptor *rsp;
+ struct random_adaptors *rpp;
+ const char *name;
+ int error;
+
+ name = NULL;
+ rsp = random_get_active_adaptor();
+
+ if (rsp != NULL) {
+ sx_slock(&adaptors_lock);
+
+ LIST_FOREACH(rpp, &adaptors, entries) {
+ if (rpp->rsp == rsp)
+ name = rpp->name;
+ }
+
+ sx_sunlock(&adaptors_lock);
+ }
+
+ if (rsp == NULL || name == NULL) {
+ error = SYSCTL_OUT(req, "", 0);
+ } else {
+ error = SYSCTL_OUT(req, name, strlen(name));
+ }
+
+ return (error);
+}
+
static void
random_adaptors_init(void *unused)
{
@@ -127,6 +332,11 @@ random_adaptors_init(void *unused)
NULL, 0, random_sysctl_adaptors_handler, "",
"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, "",
+ "Active Random Number Generator Adaptor");
+
sx_init(&adaptors_lock, "random_adaptors");
}
OpenPOWER on IntegriCloud