All of lore.kernel.org
 help / color / mirror / Atom feed
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


             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.