diff options
Diffstat (limited to 'sys/dev/random/random_adaptors.c')
-rw-r--r-- | sys/dev/random/random_adaptors.c | 228 |
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"); } |