random(4): Restore availability tradeoff prior to r346250

As discussed in that commit message, it is a dangerous default.  But the
safe default causes enough pain on a variety of platforms that for now,
restore the prior default.

Some of this is self-induced pain we should/could do better about; for
example, programmatic CI systems and VM managers should introduce entropy
from the host for individual VM instances.  This is considered a future work
item.

On modern x86 and Power9 systems, this may be wholly unnecessary after
D19928 lands (even in the non-ideal case where early /boot/entropy is
unavailable), because they have fast hardware random sources available early
in boot.  But D19928 is not yet landed and we have a host of architectures
which do not provide fast random sources.

This change adds several tunables and diagnostic sysctls, documented
thoroughly in UPDATING and sys/dev/random/random_infra.c.

PR:		230875 (reopens)
Reported by:	adrian, jhb, imp, and probably others
Reviewed by:	delphij, imp (earlier version), markm (earlier version)
Discussed with:	adrian
Approved by:	secteam(delphij)
Relnotes:	yeah
Security:	related
Differential Revision:	https://reviews.freebsd.org/D19944
This commit is contained in:
Conrad Meyer 2019-04-18 20:48:54 +00:00
parent db92a6cd51
commit 3782136ff1
7 changed files with 159 additions and 18 deletions

View File

@ -31,6 +31,30 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 13.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
20190418:
The following knobs have been added related to tradeoffs between
safe use of the random device and availability in the absence of
entropy:
kern.random.initial_seeding.bypass_before_seeding: tunable; set
non-zero to bypass the random device prior to seeding, or zero to
block random requests until the random device is initially seeded.
For now, set to 1 (unsafe) by default to restore pre-r346250 boot
availability properties.
kern.random.initial_seeding.read_random_bypassed_before_seeding:
read-only diagnostic sysctl that is set when bypass is enabled and
read_random(9) is bypassed, to enable programmatic handling of this
initial condition, if desired.
kern.random.initial_seeding.arc4random_bypassed_before_seeding:
Similar to the above, but for for arc4random(9) initial seeding.
kern.random.initial_seeding.disable_bypass_warnings: tunable; set
non-zero to disable warnings in dmesg when the same conditions are
met as for the diagnostic sysctls above. Defaults to zero, i.e.,
produce warnings in dmesg when the conditions are met.
20190416:
The tunable "security.stack_protect.permit_nonrandom_cookies" may be
set to a non-zero value to boot systems that do not provide early

View File

@ -43,7 +43,61 @@ __FBSDID("$FreeBSD$");
#include <dev/random/randomdev.h>
/* Set up the sysctl root node for the entropy device */
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Cryptographically Secure Random Number Generator");
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0,
"Cryptographically Secure Random Number Generator");
SYSCTL_NODE(_kern_random, OID_AUTO, initial_seeding, CTLFLAG_RW, 0,
"Initial seeding control and information");
/*
* N.B., this is a dangerous default, but it matches the behavior prior to
* r346250 (and, say, OpenBSD -- although they get some guaranteed saved
* entropy from the prior boot because of their KARL system, on RW media).
*/
bool random_bypass_before_seeding = true;
SYSCTL_BOOL(_kern_random_initial_seeding, OID_AUTO,
bypass_before_seeding, CTLFLAG_RDTUN, &random_bypass_before_seeding,
0, "If set non-zero, bypass the random device in requests for random "
"data when the random device is not yet seeded. This is considered "
"dangerous. Ordinarily, the random device will block requests until "
"it is seeded by sufficient entropy.");
/*
* This is a read-only diagnostic that reports the combination of the former
* tunable and actual bypass. It is intended for programmatic inspection by
* userspace administrative utilities after boot.
*/
bool read_random_bypassed_before_seeding = false;
SYSCTL_BOOL(_kern_random_initial_seeding, OID_AUTO,
read_random_bypassed_before_seeding, CTLFLAG_RD,
&read_random_bypassed_before_seeding, 0, "If non-zero, the random device "
"was bypassed because the 'bypass_before_seeding' knob was enabled and a "
"request was submitted prior to initial seeding.");
/*
* This is a read-only diagnostic that reports the combination of the former
* tunable and actual bypass for arc4random initial seeding. It is intended
* for programmatic inspection by userspace administrative utilities after
* boot.
*/
bool arc4random_bypassed_before_seeding = false;
SYSCTL_BOOL(_kern_random_initial_seeding, OID_AUTO,
arc4random_bypassed_before_seeding, CTLFLAG_RD,
&arc4random_bypassed_before_seeding, 0, "If non-zero, the random device "
"was bypassed when initially seeding the kernel arc4random(9), because "
"the 'bypass_before_seeding' knob was enabled and a request was submitted "
"prior to initial seeding.");
/*
* This knob is for users who do not want additional warnings in their logs
* because they intend to handle bypass by inspecting the status of the
* diagnostic sysctls.
*/
bool random_bypass_disable_warnings = false;
SYSCTL_BOOL(_kern_random_initial_seeding, OID_AUTO,
disable_bypass_warnings, CTLFLAG_RDTUN,
&random_bypass_disable_warnings, 0, "If non-zero, do not log a warning "
"if the 'bypass_before_seeding' knob is enabled and a request is "
"submitted prior to initial seeding.");
MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures");

View File

@ -236,11 +236,15 @@ READ_RANDOM_UIO(struct uio *uio, bool nonblock)
}
/*-
* Kernel API version of read_random().
* This is similar to random_alg_read(),
* except it doesn't interface with uio(9).
* It cannot assumed that random_buf is a multiple of
* RANDOM_BLOCKSIZE bytes.
* Kernel API version of read_random(). This is similar to read_random_uio(),
* except it doesn't interface with uio(9). It cannot assumed that random_buf
* is a multiple of RANDOM_BLOCKSIZE bytes.
*
* If the tunable 'kern.random.initial_seeding.bypass_before_seeding' is set
* non-zero, silently fail to emit random data (matching the pre-r346250
* behavior). If read_random is called prior to seeding and bypassed because
* of this tunable, the condition is reported in the read-only sysctl
* 'kern.random.initial_seeding.read_random_bypassed_before_seeding'.
*/
void
READ_RANDOM(void *random_buf, u_int len)
@ -249,12 +253,31 @@ READ_RANDOM(void *random_buf, u_int len)
KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__));
p_random_alg_context->ra_pre_read();
/* (Un)Blocking logic */
if (!p_random_alg_context->ra_seeded())
(void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE);
read_rate_increment(roundup2(len, sizeof(uint32_t)));
if (len == 0)
return;
/* (Un)Blocking logic */
if (__predict_false(!p_random_alg_context->ra_seeded())) {
if (random_bypass_before_seeding) {
if (!read_random_bypassed_before_seeding) {
if (!random_bypass_disable_warnings)
printf("read_random: WARNING: bypassing"
" request for random data because "
"the random device is not yet "
"seeded and the knob "
"'bypass_before_seeding' was "
"enabled.\n");
read_random_bypassed_before_seeding = true;
}
/* Avoid potentially leaking stack garbage */
memset(random_buf, 0, len);
return;
}
(void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE);
}
read_rate_increment(roundup2(len, sizeof(uint32_t)));
/*
* The underlying generator expects multiples of
* RANDOM_BLOCKSIZE.

View File

@ -37,6 +37,7 @@
#ifdef SYSCTL_DECL /* from sysctl.h */
SYSCTL_DECL(_kern_random);
SYSCTL_DECL(_kern_random_initial_seeding);
#define RANDOM_CHECK_UINT(name, min, max) \
static int \
@ -55,6 +56,11 @@ random_check_uint_##name(SYSCTL_HANDLER_ARGS) \
MALLOC_DECLARE(M_ENTROPY);
extern bool random_bypass_before_seeding;
extern bool read_random_bypassed_before_seeding;
extern bool arc4random_bypassed_before_seeding;
extern bool random_bypass_disable_warnings;
#endif /* _KERNEL */
struct harvest_event;

View File

@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$");
#include <sys/time.h>
#include <crypto/chacha20/chacha.h>
#include <crypto/sha2/sha256.h>
#include <dev/random/randomdev.h>
#include <machine/cpu.h>
#define CHACHA20_RESEED_BYTES 65536
#define CHACHA20_RESEED_SECONDS 300
@ -77,12 +80,43 @@ chacha20_randomstir(struct chacha20_s *chacha20)
struct timeval tv_now;
u_int8_t key[CHACHA20_KEYBYTES];
/*
* If the loader(8) did not have an entropy stash from the previous
* shutdown to load, then we will block. The answer is to make sure
* there is an entropy stash at shutdown time.
*/
read_random(key, CHACHA20_KEYBYTES);
if (__predict_false(random_bypass_before_seeding && !is_random_seeded())) {
SHA256_CTX ctx;
uint64_t cc;
uint32_t fver;
if (!arc4random_bypassed_before_seeding) {
arc4random_bypassed_before_seeding = true;
if (!random_bypass_disable_warnings)
printf("arc4random: WARNING: initial seeding "
"bypassed the cryptographic random device "
"because it was not yet seeded and the "
"knob 'bypass_before_seeding' was "
"enabled.\n");
}
/* Last ditch effort to inject something in a bad condition. */
cc = get_cyclecount();
SHA256_Init(&ctx);
SHA256_Update(&ctx, key, sizeof(key));
SHA256_Update(&ctx, &cc, sizeof(cc));
fver = __FreeBSD_version;
SHA256_Update(&ctx, &fver, sizeof(fver));
_Static_assert(sizeof(key) == SHA256_DIGEST_LENGTH,
"make sure 256 bits is still 256 bits");
SHA256_Final(key, &ctx);
} else {
/*
* If the loader(8) did not have an entropy stash from the
* previous shutdown to load, then we will block. The answer is
* to make sure there is an entropy stash at shutdown time.
*
* On the other hand, if the random_bypass_before_seeding knob
* was set and we landed in this branch, we know this won't
* block because we know the random device is seeded.
*/
read_random(key, CHACHA20_KEYBYTES);
}
getmicrouptime(&tv_now);
mtx_lock(&chacha20->mtx);
chacha_keysetup(&chacha20->ctx, key, CHACHA20_KEYBYTES*8);

View File

@ -133,5 +133,5 @@ device loop
device ether
#device md
#device bpf
#device random
device random
#device if_bridge

View File

@ -60,7 +60,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 1300019 /* Master, propagated to newvers */
#define __FreeBSD_version 1300020 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,