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:
parent
d9340a46aa
commit
4c79b5f69c
24
UPDATING
24
UPDATING
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -133,5 +133,5 @@ device loop
|
||||
device ether
|
||||
#device md
|
||||
#device bpf
|
||||
#device random
|
||||
device random
|
||||
#device if_bridge
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user