lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 27 May 2019 12:26:28 +0000
From:   Naveen Nathan <naveen@...tninja.net>
To:     Theodore Ts'o <tytso@....edu>
Cc:     Arnd Bergmann <arnd@...db.de>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        "Jason A. Donenfeld" <Jason@...c4.com>,
        Kevin Easton <kevin@...rana.org>, linux-kernel@...r.kernel.org
Subject: [PATCH] random: urandom reads block when CRNG is not initialized.

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@...tninja.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ