From: Naveen Nathan <naveen@lastninja.net>
To: Theodore Ts'o <tytso@mit.edu>
Cc: Arnd Bergmann <arnd@arndb.de>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Jason A. Donenfeld" <Jason@zx2c4.com>,
Kevin Easton <kevin@guarana.org>,
linux-kernel@vger.kernel.org
Subject: [PATCH] random: urandom reads block when CRNG is not initialized.
Date: Mon, 27 May 2019 12:26:28 +0000 [thread overview]
Message-ID: <20190527122627.GA15618@u> (raw)
Adds a compile-time option to ensure urandom reads block until
the cryptographic random number generator (CRNG) is initialized.
This fixes a long standing security issue, the so called boot-time
entropy hole, where systems (particularly headless and embededd)
generate cryptographic keys before the CRNG has been iniitalised,
as exhibited in the work at https://factorable.net/.
This is deliberately a compile-time option without a corresponding
command line option to toggle urandom blocking behavior to prevent
system builders shooting themselves in the foot by
accidently/deliberately/maliciously toggling the option off in
production builds.
Signed-off-by: Naveen Nathan <naveen@lastninja.net>
---
drivers/char/Kconfig | 9 ++++++++
drivers/char/random.c | 48 +++++++++++++++++++++++++++++++++++--------
2 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 466ebd84ad17..9a09fdb37040 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -559,6 +559,15 @@ config ADI
endmenu
+config ALWAYS_SECURE_URANDOM
+ bool "Ensure /dev/urandom always produces secure randomness"
+ default n
+ help
+ Ensure reads to /dev/urandom block until Linux CRNG is initialized.
+ All reads after initialization are non-blocking. This protects
+ readers of /dev/urandom from receiving insecure randomness on cold
+ start when the entropy pool isn't initially filled.
+
config RANDOM_TRUST_CPU
bool "Trust the CPU manufacturer to initialize Linux's CRNG"
depends on X86 || S390 || PPC
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 5d5ea4ce1442..c2bca7fbca5e 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -473,6 +473,10 @@ static const struct poolinfo {
*/
static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
+#if IS_ENABLED(CONFIG_ALWAYS_SECURE_URANDOM)
+static DECLARE_WAIT_QUEUE_HEAD(urandom_read_wait);
+static DECLARE_WAIT_QUEUE_HEAD(urandom_write_wait);
+#endif
static struct fasync_struct *fasync;
static DEFINE_SPINLOCK(random_ready_list_lock);
@@ -1966,15 +1970,23 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
static int maxwarn = 10;
int ret;
- if (!crng_ready() && maxwarn > 0) {
- maxwarn--;
- if (__ratelimit(&urandom_warning))
- printk(KERN_NOTICE "random: %s: uninitialized "
- "urandom read (%zd bytes read)\n",
- current->comm, nbytes);
- spin_lock_irqsave(&primary_crng.lock, flags);
- crng_init_cnt = 0;
- spin_unlock_irqrestore(&primary_crng.lock, flags);
+ if (!crng_ready()) {
+ if (IS_ENABLED(CONFIG_ALWAYS_SECURE_URANDOM)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ ret = wait_for_random_bytes();
+ if (unlikely(ret))
+ return ret;
+ } else if (maxwarn > 0) {
+ maxwarn--;
+ if (__ratelimit(&urandom_warning))
+ pr_notice("random: %s: uninitialized "
+ "urandom read (%zd bytes read)\n",
+ current->comm, nbytes);
+ spin_lock_irqsave(&primary_crng.lock, flags);
+ crng_init_cnt = 0;
+ spin_unlock_irqrestore(&primary_crng.lock, flags);
+ }
}
nbytes = min_t(size_t, nbytes, INT_MAX >> (ENTROPY_SHIFT + 3));
ret = extract_crng_user(buf, nbytes);
@@ -1997,6 +2009,21 @@ random_poll(struct file *file, poll_table * wait)
return mask;
}
+#if IS_ENABLED(CONFIG_ALWAYS_SECURE_URANDOM)
+static __poll_t
+urandom_poll(struct file *file, poll_table *wait)
+{
+ __poll_t mask;
+
+ poll_wait(file, &urandom_read_wait, wait);
+ poll_wait(file, &urandom_write_wait, wait);
+ mask = EPOLLOUT | EPOLLWRNORM;
+ if (crng_ready())
+ mask |= EPOLLIN | EPOLLRDNORM;
+ return mask;
+}
+#endif
+
static int
write_pool(struct entropy_store *r, const char __user *buffer, size_t count)
{
@@ -2113,6 +2140,9 @@ const struct file_operations random_fops = {
const struct file_operations urandom_fops = {
.read = urandom_read,
.write = random_write,
+#if IS_ENABLED(CONFIG_ALWAYS_SECURE_URANDOM)
+ .poll = urandom_poll,
+#endif
.unlocked_ioctl = random_ioctl,
.fasync = random_fasync,
.llseek = noop_llseek,
--
2.17.1
next reply other threads:[~2019-05-27 12:26 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-27 12:26 Naveen Nathan [this message]
2019-05-27 14:06 ` [PATCH] random: urandom reads block when CRNG is not initialized Theodore Ts'o
2019-05-27 15:35 ` Naveen Nathan
2019-05-27 15:43 ` Jason A. Donenfeld
2019-05-27 17:05 ` Theodore Ts'o
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190527122627.GA15618@u \
--to=naveen@lastninja.net \
--cc=Jason@zx2c4.com \
--cc=arnd@arndb.de \
--cc=gregkh@linuxfoundation.org \
--cc=kevin@guarana.org \
--cc=linux-kernel@vger.kernel.org \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.