Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/kern entropy(9): Reintroduce netbsd<=9 time-delta estima...
details: https://anonhg.NetBSD.org/src/rev/6fd1342ab0d2
branches: trunk
changeset: 377200:6fd1342ab0d2
user: riastradh <riastradh%NetBSD.org@localhost>
date: Fri Jun 30 21:42:05 2023 +0000
description:
entropy(9): Reintroduce netbsd<=9 time-delta estimator for unblocking.
The system will (in a subsequent change) by default block for this
condition before almost all of userland is running (including
/etc/rc.d/sshd key generation). That way, a never-blocking
getentropy(3) API will never return any data without at least
best-effort entropy like netbsd<=9 did to applications except in
single-user mode (where you have to be careful about everything
anyway) or in the few processes that run before a seed can even be
loaded (where blocking indefinitely, e.g. when generating a stack
protector cookie in libc, could pose a severe availability problem
that can't be configured away, but where the security impact is low).
However, (in another subsequent change) we will continue to use
_only_ HWRNG driver estimates and seed estimates, and _not_
time-delta estimator, for _warning_ about security in motd, daily
security report, etc. And if HWRNG/seed provides enough entropy
before time-delta estimator does, that will unblock /dev/random too.
The result is:
- Machines with HWRNG or seed won't warn about entropy and will
essentially never block -- even on first boot without a seed, it
will take only as long as the fastest HWRNG to unblock.
- Machines with neither HWRNG nor seed:
. will warn about entropy, giving feedback about security;
and
. will avoid returning anything more predictable than netbsd<=9;
but
. won't block (much) longer than netbsd<=9 would (and won't block
again after blocking once, except with kern.entropy.depletion=1 for
testing).
(The threshold for unblocking is now somewhat higher than before:
512 samples that pass the time-delta estimator, rather than 80 as
it used to be.)
And, of course, adding a seed (or HWRNG) will prevent both warnings
and blocking.
The mechanism is:
1. /dev/random will block until _either_
(a) enough bits of entropy (256) from reliable sources have been
added to the pool, _or_
(b) enough samples have been added from any sources (512), passing
the old time-delta entropy estimator, that the possible
security benefit doesn't justify holding up availability any
longer (`best effort'), except on systems with higher security
requirements like securelevel=2 which can disable non-HWRNG,
non-seed sources with rndctl_flags in rc.conf(5).
2. dmesg will report `entropy: ready' when 1(a) is satisfied, but if
1(b) is satisfied first, it will report `entropy: best effort', so
the concise log messages will reflect the timing and whether in
any period of time any of the system might be relying on best
effort entropy.
3. The sysctl knob kern.entropy.needed (and the ioctl RNDGETPOOLSTAT
variable rndpoolstat_t::added) still reflects the number of bits
of entropy from reliable sources, so we can still use this to
suggest regenerating ssh keys.
This matters on platforms that can only be reached, after flashing
an installation image, by sshing in over a (private) network, like
small network appliances or remote virtual machines without
(interactive) serial consoles. If we blocked indefinitely at boot
when generating ssh keys, such platforms would be unusable. This
way, platforms are usable, but operators can still be advised at
login time to regenerate keys as soon as they can actually load
entropy onto the system, e.g. with rndctl(8) on a seed file copied
from a local machine over the (private) network.
4. On machines without HWRNG, using a seed file still suppresses
warnings for users who need more confident security. But it is no
longer necessary for availability.
This is a compromise between availability and security:
- The security mechanism of blocking indefinitely on machines without
HWRNG hurts availability too much, as painful experience over the
multiple years since I made the mistake of introducing it have
shown. (Sorry!)
- The other main alternative, not having a blocking path at all (as I
pushed for, and as OpenBSD has done for a long time) could
potentially reduce security vs netbsd<=9, and would run against the
expectations set by many popular operating systems to the severe
detriment of public perception of NetBSD security.
Even though we can't _confidently_ assess enough entropy from, e.g.,
sampling interrupt timings, this is the traditional behaviour that
most operating systems provide -- and the result here is a net
nondecrease in security over netbsd<=9, because all paths from the
entropy pool to userland now have at least as high a standard before
returning data as they did in netbsd<=9.
PR kern/55641
PR pkg/55847
PR kern/57185
https://mail-index.netbsd.org/current-users/2020/09/02/msg039470.html
https://mail-index.netbsd.org/current-users/2020/11/21/msg039931.html
https://mail-index.netbsd.org/current-users/2020/12/05/msg040019.html
XXX pullup-10
diffstat:
share/man/man7/entropy.7 | 61 +++--
sys/kern/kern_clock.c | 10 +-
sys/kern/kern_entropy.c | 460 ++++++++++++++++++++++++++++++++++------------
3 files changed, 373 insertions(+), 158 deletions(-)
diffs (truncated from 1072 to 300 lines):
diff -r 7f9355b88756 -r 6fd1342ab0d2 share/man/man7/entropy.7
--- a/share/man/man7/entropy.7 Fri Jun 30 21:39:54 2023 +0000
+++ b/share/man/man7/entropy.7 Fri Jun 30 21:42:05 2023 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: entropy.7,v 1.8 2023/03/23 12:41:43 uwe Exp $
+.\" $NetBSD: entropy.7,v 1.9 2023/06/30 21:42:05 riastradh Exp $
.\"
.\" Copyright (c) 2021 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -111,13 +111,8 @@ for security, thanks to modern cryptogra
.Pp
To detect potentially insecure systems,
.Nx
-records how many bits it needs to achieve the full 256 bits, exposed
-via the
-.Xr sysctl 7
-variable
-.Li kern.entropy.needed ,
-and takes measures to alert the operator if there isn't definitely
-enough for security:
+takes measures to alert the operator if there isn't definitely enough
+for security:
.Bl -bullet
.It
.Nx
@@ -125,29 +120,41 @@ issues warnings on the console if there'
programs need it; see
.Xr rnd 4 .
.It
-The daily security report includes an alert if there's not enough
+The
+.Xr motd 5
+has a warning if there was not enough entropy network daemons such as
+.Xr sshd 8
+first generated keys.
+.It
+The daily security report includes an alert if there's still not enough
entropy; see
.Xr security.conf 5 .
-.It
-The operator can set
-.Ql entropy=check
-in
-.Xr rc.conf 5
-so that
-.Nx
-will refuse to boot to multiuser unless there is enough entropy, or set
-.Ql entropy=wait
-so that
-.Nx
-will wait for entropy before booting to multiuser (with the caveat that
-it may cause boot to hang forever).
.El
.Pp
-Since it is difficult to confidently model the unpredictability of most
-physical systems, only devices specifically designed to be hardware
-random number generators count toward
-.Nx Ns 's
-estimate of the entropy.
+Since it is hard to know how unpredictable most physical systems are,
+only devices specifically designed to be hardware random number
+generators, or a seed file stored on disk, count toward these alerts.
+.Pp
+At boot,
+.Nx
+will wait, when
+.Ql entropy=wait
+is set in
+.Xr rc.conf 5 ,
+or fail to single-user mode, when
+.Ql entropy=check
+is set, if there is not enough entropy from
+.Em any
+sources, including devices not designed to be unpredictable, such as
+the CPU cycle counter sampled by a periodic timer, provided the samples
+pass a simple filter called the
+.Sq entropy estimator ,
+like other operating systems.
+Sources known to be predictable, which could give a false sense of
+security, can be disabled from unblocking boot by setting
+.Li rndctl_flags
+in
+.Xr rc.conf 5 .
.Pp
Many new computers have hardware random number generators, such as
RDRAND/RDSEED in Intel/AMD CPUs, or ARMv8.5-RNDRRS;
diff -r 7f9355b88756 -r 6fd1342ab0d2 sys/kern/kern_clock.c
--- a/sys/kern/kern_clock.c Fri Jun 30 21:39:54 2023 +0000
+++ b/sys/kern/kern_clock.c Fri Jun 30 21:42:05 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_clock.c,v 1.148 2022/03/19 14:34:47 riastradh Exp $ */
+/* $NetBSD: kern_clock.c,v 1.149 2023/06/30 21:42:05 riastradh Exp $ */
/*-
* Copyright (c) 2000, 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -69,7 +69,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.148 2022/03/19 14:34:47 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.149 2023/06/30 21:42:05 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_dtrace.h"
@@ -284,12 +284,14 @@ initclocks(void)
rndsource_setcb(&hardclockrnd.source, clockrnd_get, &hardclockrnd);
rnd_attach_source(&hardclockrnd.source, "hardclock", RND_TYPE_SKEW,
- RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+ RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|RND_FLAG_HASCB);
if (stathz) {
rndsource_setcb(&statclockrnd.source, clockrnd_get,
&statclockrnd);
rnd_attach_source(&statclockrnd.source, "statclock",
- RND_TYPE_SKEW, RND_FLAG_COLLECT_TIME|RND_FLAG_HASCB);
+ RND_TYPE_SKEW,
+ (RND_FLAG_COLLECT_TIME|RND_FLAG_ESTIMATE_TIME|
+ RND_FLAG_HASCB));
}
}
diff -r 7f9355b88756 -r 6fd1342ab0d2 sys/kern/kern_entropy.c
--- a/sys/kern/kern_entropy.c Fri Jun 30 21:39:54 2023 +0000
+++ b/sys/kern/kern_entropy.c Fri Jun 30 21:42:05 2023 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_entropy.c,v 1.61 2023/05/24 20:22:23 riastradh Exp $ */
+/* $NetBSD: kern_entropy.c,v 1.62 2023/06/30 21:42:05 riastradh Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -62,20 +62,13 @@
* facilitates an operator explicitly causing everything to
* reseed by sysctl -w kern.entropy.consolidate=1.
*
- * * No entropy estimation based on the sample values, which is a
- * contradiction in terms and a potential source of side
- * channels. It is the responsibility of the driver author to
- * study how predictable the physical source of input can ever
- * be, and to furnish a lower bound on the amount of entropy it
- * has.
- *
* * Entropy depletion is available for testing (or if you're into
* that sort of thing), with sysctl -w kern.entropy.depletion=1;
* the logic to support it is small, to minimize chance of bugs.
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.61 2023/05/24 20:22:23 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_entropy.c,v 1.62 2023/06/30 21:42:05 riastradh Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -122,6 +115,10 @@
#include <machine/cpu_counter.h>
#endif
+#define MINENTROPYBYTES ENTROPY_CAPACITY
+#define MINENTROPYBITS (MINENTROPYBYTES*NBBY)
+#define MINSAMPLES (2*MINENTROPYBITS)
+
/*
* struct entropy_cpu
*
@@ -138,7 +135,8 @@ struct entropy_cpu {
struct evcnt intrtrunc;
} *ec_evcnt;
struct entpool *ec_pool;
- unsigned ec_pending;
+ unsigned ec_bitspending;
+ unsigned ec_samplespending;
bool ec_locked;
};
@@ -161,6 +159,7 @@ struct rndsource_cpu {
unsigned rc_entropybits;
unsigned rc_timesamples;
unsigned rc_datasamples;
+ rnd_delta_t rc_timedelta;
};
/*
@@ -173,8 +172,10 @@ struct rndsource_cpu {
struct {
kmutex_t lock; /* covers all global state */
struct entpool pool; /* global pool for extraction */
- unsigned needed; /* (A) needed globally */
- unsigned pending; /* (A) pending in per-CPU pools */
+ unsigned bitsneeded; /* (A) needed globally */
+ unsigned bitspending; /* pending in per-CPU pools */
+ unsigned samplesneeded; /* (A) needed globally */
+ unsigned samplespending; /* pending in per-CPU pools */
unsigned timestamp; /* (A) time of last consolidation */
unsigned epoch; /* (A) changes when needed -> 0 */
kcondvar_t cv; /* notifies state changes */
@@ -192,7 +193,8 @@ struct {
bool seeded; /* true if seed file already loaded */
} entropy_global __cacheline_aligned = {
/* Fields that must be initialized when the kernel is loaded. */
- .needed = ENTROPY_CAPACITY*NBBY,
+ .bitsneeded = MINENTROPYBITS,
+ .samplesneeded = MINSAMPLES,
.epoch = (unsigned)-1, /* -1 means entropy never consolidated */
.sources = LIST_HEAD_INITIALIZER(entropy_global.sources),
.stage = ENTROPY_COLD,
@@ -249,11 +251,11 @@ static struct sysctllog *entropy_sysctl
static void entropy_init_cpu(void *, void *, struct cpu_info *);
static void entropy_fini_cpu(void *, void *, struct cpu_info *);
static void entropy_account_cpu(struct entropy_cpu *);
-static void entropy_enter(const void *, size_t, unsigned);
-static bool entropy_enter_intr(const void *, size_t, unsigned);
+static void entropy_enter(const void *, size_t, unsigned, bool);
+static bool entropy_enter_intr(const void *, size_t, unsigned, bool);
static void entropy_softintr(void *);
static void entropy_thread(void *);
-static uint32_t entropy_pending(void);
+static bool entropy_pending(void);
static void entropy_pending_cpu(void *, void *, struct cpu_info *);
static void entropy_do_consolidate(void);
static void entropy_consolidate_xc(void *, void *);
@@ -264,7 +266,7 @@ static void filt_entropy_read_detach(str
static int filt_entropy_read_event(struct knote *, long);
static int entropy_request(size_t, int);
static void rnd_add_data_1(struct krndsource *, const void *, uint32_t,
- uint32_t, uint32_t);
+ uint32_t, bool, uint32_t);
static unsigned rndsource_entropybits(struct krndsource *);
static void rndsource_entropybits_cpu(void *, void *, struct cpu_info *);
static void rndsource_to_user(struct krndsource *, rndsource_t *);
@@ -368,12 +370,24 @@ entropy_init(void)
/* XXX These should maybe not be readable at securelevel>0. */
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
- "needed", SYSCTL_DESCR("Systemwide entropy deficit"),
- NULL, 0, &E->needed, 0, CTL_CREATE, CTL_EOL);
+ "needed",
+ SYSCTL_DESCR("Systemwide entropy deficit (bits of entropy)"),
+ NULL, 0, &E->bitsneeded, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
- "pending", SYSCTL_DESCR("Entropy pending on CPUs"),
- NULL, 0, &E->pending, 0, CTL_CREATE, CTL_EOL);
+ "pending",
+ SYSCTL_DESCR("Number of bits of entropy pending on CPUs"),
+ NULL, 0, &E->bitspending, 0, CTL_CREATE, CTL_EOL);
+ sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
+ "samplesneeded",
+ SYSCTL_DESCR("Systemwide entropy deficit (samples)"),
+ NULL, 0, &E->samplesneeded, 0, CTL_CREATE, CTL_EOL);
+ sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
+ "samplespending",
+ SYSCTL_DESCR("Number of samples pending on CPUs"),
+ NULL, 0, &E->samplespending, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(&entropy_sysctllog, 0, &entropy_sysctlroot, NULL,
CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT,
"epoch", SYSCTL_DESCR("Entropy epoch"),
@@ -403,7 +417,7 @@ entropy_init(void)
/* Enter the boot cycle count to get started. */
extra[i++] = entropy_timer();
KASSERT(i == __arraycount(extra));
- entropy_enter(extra, sizeof extra, 0);
+ entropy_enter(extra, sizeof extra, /*nbits*/0, /*count*/false);
explicit_memset(extra, 0, sizeof extra);
/* We are now ready for multi-threaded operation. */
@@ -496,7 +510,8 @@ entropy_init_cpu(void *ptr, void *cookie
ec->ec_evcnt = kmem_alloc(sizeof(*ec->ec_evcnt), KM_SLEEP);
ec->ec_pool = kmem_zalloc(sizeof(*ec->ec_pool), KM_SLEEP);
- ec->ec_pending = 0;
+ ec->ec_bitspending = 0;
+ ec->ec_samplespending = 0;
ec->ec_locked = false;
/* XXX ci_cpuname may not be initialized early enough. */
@@ -671,7 +686,7 @@ entropy_bootrequest(void)
* This is harmless overkill if the bootloader provided a seed.
*/
mutex_enter(&E->lock);
- error = entropy_request(ENTROPY_CAPACITY, ENTROPY_WAIT);
+ error = entropy_request(MINENTROPYBYTES, ENTROPY_WAIT);
KASSERT(error == 0);
mutex_exit(&E->lock);
}
@@ -721,7 +736,7 @@ bool
entropy_ready(void)
{
- return atomic_load_relaxed(&E->needed) == 0;
+ return atomic_load_relaxed(&E->bitsneeded) == 0;
}
/*
@@ -746,7 +761,7 @@ entropy_account_cpu(struct entropy_cpu *
{
struct entropy_cpu_lock lock;
struct entropy_cpu *ec0;
- unsigned diff;
+ unsigned bitsdiff, samplesdiff;
Home |
Main Index |
Thread Index |
Old Index