NetBSD Problem Report #56905
From gson@gson.org Wed Jun 29 15:58:42 2022
Return-Path: <gson@gson.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id AE7831A921F
for <gnats-bugs@gnats.NetBSD.org>; Wed, 29 Jun 2022 15:58:42 +0000 (UTC)
Message-Id: <20220629155833.3D38C2547A7@guava.gson.org>
Date: Wed, 29 Jun 2022 18:58:33 +0300 (EEST)
From: gson@gson.org (Andreas Gustafsson)
Reply-To: gson@gson.org (Andreas Gustafsson)
To: gnats-bugs@NetBSD.org
Subject: getentropy() may return predictable data
X-Send-Pr-Version: 3.95
>Number: 56905
>Category: lib
>Synopsis: getentropy() may return predictable data
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Jun 29 16:00:00 +0000 2022
>Last-Modified: Fri Jun 30 21:45:07 +0000 2023
>Originator: Andreas Gustafsson
>Release: NetBSD-current, source date >= 2022.05.31.13.42.59
>Organization:
>Environment:
System: NetBSD
Architecture: any
Machine: any
>Description:
On 2022-05-31, riastradh@ committed a change to re-enable the
getentropy(3) library call which nia@ had added on 2020-05-06 and
then disabled on 2020-09-23.
When the system lacks entropy, this implementation of getentropy()
will succeed and therefore return data known by the system to be
predictable, rather than failing or blocking until entropy becomes
available. I consider this to be a security vulnerability, similar to
the one in ssh-keygen I reported in PR 55659 but this time affecting
any third-party application that use getentropy() for cryptographic
purposes.
I understand that not everyone agrees with this assessment, and as
the commit message says, "Discussion of details of the semantics, as
interpreted by NetBSD, is ongoing". Nonetheless, when I see what
I believe is a security vulnerability, I feel it is my duty to report
it, and if possible, fix it. And because I believe the discussion
about this issue should be public, I'm filing this in category lib
rather than category security, as PRs in category security and their
follow-up messages don't get publicly posted to netbsd-bugs even if
you explicitly set "Confidential: no".
>How-To-Repeat:
Boot a -current system with no entropy source. Call getentropy().
Find that it succeeds.
>Fix:
The following patch fixes the issue by making getentropy() block
until entropy is available. It passes all the tests in
/usr/src/tests/lib/libc/gen/t_getentropy.c.
Index: getentropy.c
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/getentropy.c,v
retrieving revision 1.3
diff -u -r1.3 getentropy.c
--- getentropy.c 31 May 2022 13:42:59 -0000 1.3
+++ getentropy.c 28 Jun 2022 08:33:27 -0000
@@ -35,8 +35,9 @@
#include "namespace.h"
#include <sys/param.h>
-#include <sys/sysctl.h>
+#include <sys/random.h>
+#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
@@ -50,8 +51,7 @@
int
getentropy(void *buf, size_t buflen)
{
- size_t len = buflen;
- int name[2] = { CTL_KERN, KERN_ARND };
+ ssize_t r;
if (buf == NULL && buflen > 0) {
errno = EFAULT;
@@ -63,5 +63,11 @@
return -1;
}
- return sysctl(name, 2, buf, &len, NULL, 0);
+ do {
+ r = getrandom(buf, buflen, 0);
+ } while (r == -1 && errno == EINTR);
+
+ assert(r == -1 || r == (ssize_t)buflen);
+
+ return (int)r;
}
>Audit-Trail:
From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: lib-bug-people@netbsd.org, gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Wed, 29 Jun 2022 12:13:47 -0400
But it is also supposed to return 0 on success, not the number of bytes.
christos
From: Robert Elz <kre@munnari.OZ.AU>
To: Christos Zoulas <christos@zoulas.com>
Cc: gnats-bugs@netbsd.org, lib-bug-people@netbsd.org, gnats-admin@netbsd.org,
netbsd-bugs@netbsd.org
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Wed, 29 Jun 2022 23:59:21 +0700
It is also deliberately intended to not block, that was the whole
point I think.
As I understand it, the idea is that if you care about security,
you make sure sufficient entropy is available when the system boots,
and there isn't an issue with it the way it is now.
If you don't care (that much) about security then you might not do
that, but you also are very unlikely to care that the initial entropy
might not be as unpredictable as security conscious people might demand.
But what no-one wants, is processes hanging forever because they're waiting
on entropy arriving, from somewhere unknown, which no-one cares (or perhaps
knows) enough about to create. That's what all the complaints have been
about. Note "forever" in the hanging - when there's no initial entropy,
and nothing providing any, the loop you're proposing adding never terminates.
The plan, which probably has not yet been implemented (as it isn't an ABI
altering change, and so isn't urgent before the branch) is to have rc.d
scripts which check (if requested) if entropy is there or not, and if not,
delay/abort the system startup until some is provided. If you care about
security, you'll have that turned on, just in case the entropy source isn't
available, and needs fixing. If you don't, you won't, and won't care.
kre
From: nia <nia@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Wed, 29 Jun 2022 17:23:55 +0000
On Wed, Jun 29, 2022 at 05:00:03PM +0000, Robert Elz wrote:
> But what no-one wants, is processes hanging forever because they're waiting
> on entropy arriving, from somewhere unknown, which no-one cares (or perhaps
> knows) enough about to create. That's what all the complaints have been
> about. Note "forever" in the hanging - when there's no initial entropy,
> and nothing providing any, the loop you're proposing adding never terminates.
Forever hanging also very much goes against the specification
accepted by the Austin Group, if you want to indicate failure
it should return -1 and set errno instead.
However that should also not be the default behavior for compatibility
raisins.
From: Andreas Gustafsson <gson@gson.org>
To: Robert Elz <kre@munnari.OZ.AU>
Cc: Christos Zoulas <christos@zoulas.com>,
gnats-bugs@netbsd.org
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Thu, 30 Jun 2022 15:24:41 +0300
Robert Elz wrote:
> It is also deliberately intended to not block, that was the whole
> point I think.
The getentropy() call originated in OpenBSD, where the question of
blocking doesn't apply because OpenBSD claims to always have
sufficient entropy available. On systems that do not make that claim,
such as NetBSD or Linux, it must have the option of blocking (or
failing) to guarantee unpredictability, and on Linux, it can block.
The text in https://www.opengroup.org/austin/docs/austin_1110.pdf does
not require non-blocking, but it does require unpredictability.
> As I understand it, the idea is that if you care about security,
> you make sure sufficient entropy is available when the system boots,
> and there isn't an issue with it the way it is now.
This idea of the user having to "care" and "make sure" does not seem
compatible with NetBSD's long-standing tradition of being secure by
default.
> If you don't care (that much) about security then you might not do
> that, but you also are very unlikely to care that the initial entropy
> might not be as unpredictable as security conscious people might demand.
>
> But what no-one wants, is processes hanging forever because they're waiting
> on entropy arriving, from somewhere unknown, which no-one cares (or perhaps
> knows) enough about to create. That's what all the complaints have been
> about. Note "forever" in the hanging - when there's no initial entropy,
> and nothing providing any, the loop you're proposing adding never terminates.
Yes, this is a real problem, but it is not specific to getentropy(),
and it should really have its own PR. We need to address the root
cause of entropy not being available, or being available but not being
counted as such, not sweep the problem under the rug one API at a
time.
One way to fix it would be to go back to recognizing interrupt timing
entropy sources as contributing a nonzero amount of entropy, as most
operating systems do, including NetBSD 9. While I sympathize in
principle with the notion that we should only count entropy from
"certifiable" sources, if that results in us having to disable
blocking, we have ended up worse off than we were in NetBSD 9, not
better. Blocking until we have met at least _some_ criterion for
sufficient entropy, even one lacking theoretical rigor, is always
better than not blocking at all.
> The plan, which probably has not yet been implemented (as it isn't an ABI
> altering change, and so isn't urgent before the branch) is to have rc.d
> scripts which check (if requested) if entropy is there or not, and if not,
> delay/abort the system startup until some is provided. If you care about
> security, you'll have that turned on, just in case the entropy source isn't
> available, and needs fixing. If you don't, you won't, and won't care.
The "if requested" part does not sound like it's secure by default.
And until or unless the plan is implemented, -current remains vulnerable.
--
Andreas Gustafsson, gson@gson.org
From: Andreas Gustafsson <gson@gson.org>
To: Christos Zoulas <christos@zoulas.com>
Cc: gnats-bugs@netbsd.org
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Thu, 30 Jun 2022 15:31:15 +0300
Christos Zoulas wrote:
> But it is also supposed to return 0 on success, not the number of bytes.
Good catch, thanks. Revised patch:
Index: getentropy.c
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/getentropy.c,v
retrieving revision 1.3
diff -u -r1.3 getentropy.c
--- getentropy.c 31 May 2022 13:42:59 -0000 1.3
+++ getentropy.c 30 Jun 2022 12:32:06 -0000
@@ -35,8 +35,9 @@
#include "namespace.h"
#include <sys/param.h>
-#include <sys/sysctl.h>
+#include <sys/random.h>
+#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
@@ -50,8 +51,7 @@
int
getentropy(void *buf, size_t buflen)
{
- size_t len = buflen;
- int name[2] = { CTL_KERN, KERN_ARND };
+ ssize_t r;
if (buf == NULL && buflen > 0) {
errno = EFAULT;
@@ -63,5 +63,14 @@
return -1;
}
- return sysctl(name, 2, buf, &len, NULL, 0);
+ do {
+ r = getrandom(buf, buflen, 0);
+ } while (r == -1 && errno == EINTR);
+
+ if (r == -1)
+ return r;
+
+ assert(r == (ssize_t)buflen);
+
+ return 0;
}
From: Andreas Gustafsson <gson@gson.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Fri, 22 Jul 2022 14:22:12 +0300
The situation is even more complicated that I thought.
I thought adding getentropy() as a new libc function would only affect
third-party software because nothing in the base system would be using
a function that did not yet exist, but it turns out I was wrong. The
OpenSSL library in base actually detects the presence of getentropy()
at run time, and now ends up calling getentropy() rather than
sysctl(KERN_ARND).
Therefore, changing getentropy() to have the proper blocking semantics
would have the side effect of causing many programs in the NetBSD base
system that rely on OpenSSL, such as ssh-keygen(1), syslogd(8), and
login(1), to also block until entropy is available. That would be a
good thing from a security perspective, and might have worked in -9
where processes blocking for entropy eventually get unblocked, but
regrettably, in -current they no longer do on some systems. Since
login(1) blocking forever would render the system unusable and not
easily recoverable, making OpenSSL block is not a realistic option
until the root cause of the blocking problems in -current, the
persistent lack of entropy (or lack of belief in the entropy that is
present), is fixed.
If the intent when adding getentropy() to libc really was just to
introduce the symbol into libc for reasons of ABI compatibility, and
not to switch OpenSSL over to it, perhaps OpenSSL could be patched to
ignore it, as in the patch below. But this would just be a stopgap
measure that effectively amounts to perpetuating a long-standing
vulnerability in OpenSSL so that a new one in getentropy() can be
fixed.
Index: src/crypto/external/bsd/openssl/dist/crypto/rand/rand_unix.c
===================================================================
RCS file: /cvsroot/src/crypto/external/bsd/openssl/dist/crypto/rand/rand_unix.c,v
retrieving revision 1.18
diff -u -r1.18 rand_unix.c
--- src/crypto/external/bsd/openssl/dist/crypto/rand/rand_unix.c 7 Jan 2022 15:50:11 -0000 1.18
+++ src/crypto/external/bsd/openssl/dist/crypto/rand/rand_unix.c 21 Jul 2022 12:16:43 -0000
@@ -370,7 +370,9 @@
* Note: Sometimes getentropy() can be provided but not implemented
* internally. So we need to check errno for ENOSYS
*/
-# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux)
+# if defined(__NetBSD__)
+ /* Avoid getentropy() while its semantics are still being debated */
+# elif defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux)
extern int getentropy(void *buffer, size_t length) __attribute__((weak));
if (getentropy != NULL) {
--
Andreas Gustafsson, gson@gson.org
From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Fri, 22 Jul 2022 21:37:43 +0700
getentropy() is being added to POSIX in the next version of the standard.
The description doesn't explicitly say it is not allowed to block,
but it does say (explicitly) that it is not a thread cancellation
point.
In general, everything which can block is either a cancellation point,
or is allowed to be (the latter for things like putc() which might call
a function which is a cancellation point, or might not).
Not being a cancellation point tells me that getentropy() is not
expected to block (ever).
It is allowed to fail - but the description of its one possible relevant
failure mode (it is also possible for it to return an error if the user
passes invalid params - asks for too many bits, for example - that's not
relevant here) is:
[ENOSYS] The system does not provide the necessary source of entropy.
which seems to me to be more a permanent failure, that is, it isn't
saying "no entropy is currently available", than one which can
ever be rectified (eg: by pausing and trying later - similating blocking
in the caller).
Blocking in getentropy() won't happen, "the proper semantics" are not that.
kre
ps: there's no getrandom() call in POSIX however.
From: Andreas Gustafsson <gson@gson.org>
To: gnats-bugs@netbsd.org
Cc: Robert Elz <kre@munnari.OZ.AU>
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Fri, 22 Jul 2022 21:03:36 +0300
Robert Elz wrote:
> getentropy() is being added to POSIX in the next version of the standard.
>
> The description doesn't explicitly say it is not allowed to block,
> but it does say (explicitly) that it is not a thread cancellation
> point.
>
> In general, everything which can block is either a cancellation point,
> or is allowed to be (the latter for things like putc() which might call
> a function which is a cancellation point, or might not).
>
> Not being a cancellation point tells me that getentropy() is not
> expected to block (ever).
Yet in FreeBSD and Linux, it does block:
https://www.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html
https://man7.org/linux/man-pages/man3/getentropy.3.html
On operating systems where the kernel CSPRNG is not guaranteed to be
seeded by the time a user process runs, such as FreeBSD, Linux, and
NetBSD, getentropy() needs to block until it is seeded in order to
meet the POSIX requirement of returning unpredictable data. If POSIX
has not anticipated this, that seems like a serious flaw in the
standard.
> It is allowed to fail - but the description of its one possible relevant
> failure mode (it is also possible for it to return an error if the user
> passes invalid params - asks for too many bits, for example - that's not
> relevant here) is:
>
> [ENOSYS] The system does not provide the necessary source of entropy.
>
> which seems to me to be more a permanent failure, that is, it isn't
> saying "no entropy is currently available", than one which can
> ever be rectified (eg: by pausing and trying later - similating blocking
> in the caller).
I do agree that ENOSYS seems to be intended for more permanent
failures.
> Blocking in getentropy() won't happen, "the proper semantics" are not that.
And I won't be using an operating system where it doesn't block (or
possibly fail) when the system knows it is lacking entropy, even if
that means the end of my 27 years as a NetBSD user. Since that is not
a decision to be made lightly, I'm sure you understand that I'm not
taking your word for this but asking core to make a formal decision
one way the other.
--
Andreas Gustafsson, gson@gson.org
From: Andreas Gustafsson <gson@gson.org>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/56905: getentropy() may return predictable data
Date: Thu, 23 Mar 2023 14:39:32 +0200
For the record, I recently received the following statement from core:
"We consider the behavior in PR 56905 a bug and we are working towards a fix."
--
Andreas Gustafsson, gson@gson.org
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/56905 CVS commit: src
Date: Fri, 30 Jun 2023 21:44:09 +0000
Module Name: src
Committed By: riastradh
Date: Fri Jun 30 21:44:09 UTC 2023
Modified Files:
src/etc/defaults: rc.conf
src/share/man/man5: rc.conf.5
Log Message:
rc.conf(5): Set entropy=wait by default.
We no longer block indefinitely -- if nothing else, the hardclock
timer should yield enough samples to unblock /dev/random on all but
the most severely deterministic machines -- so it should be generally
safe for availability to set entropy=wait.
This doesn't guarantee that HWRNG/seed has been provided before you
run ssh-keygen or call getentropy(3) in a user application, but it
does raise the security above netbsd<=9.
PR security/55659
PR lib/56905
XXX pullup-10
To generate a diff of this commit:
cvs rdiff -u -r1.163 -r1.164 src/etc/defaults/rc.conf
cvs rdiff -u -r1.192 -r1.193 src/share/man/man5/rc.conf.5
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
(Contact us)
$NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2023
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.