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