getrandom() has been created as a new and more secure interface for pseudorandom data requests. Unlike /dev/urandom, it unconditionally blocks until the entropy pool has been properly initialized. While getrandom() has no guaranteed upper bound for its waiting time, user-space has been abusing it by issuing the syscall, from shared libraries no less, during the main system boot sequence. Thus, on certain setups where there is no hwrng (embedded), or the hwrng is not trusted by some users (intel RDRAND), or sometimes it's just broken (amd RDRAND), the system boot can be *reliably* blocked. The issue is further exaggerated by recent file-system optimizations, e.g. b03755ad6f33 (ext4: make __ext4_get_inode_loc plug), which merges directory lookup code inode table IO, and thus minimizes the number of disk interrupts and entropy during boot. After that commit, a blocked boot can be reliably reproduced on a Thinkpad E480 laptop with standard ArchLinux user-space. Thus, add an optional configuration option which stops getrandom(2) from blocking, but instead returns "best efforts" randomness, which might not be random or secure at all. This can be controlled via random.getrandom_block boot command line option, and the CONFIG_RANDOM_BLOCK can be used to set the default to be blocking. Since according to the Great Penguin, only incompetent system designers would value "security" ahead of "usability", the default is to be non-blocking. In addition, modify getrandom(2) to complain loudly with a kernel warning when some userspace process is erroneously calling getrandom(2) too early during the boot process. Link: https://lkml.kernel.org/r/CAHk-=wjyH910+JRBdZf_Y9G54c1M=LBF8NKXB6vJcm9XjLnRfg@xxxxxxxxxxxxxx Link: https://lkml.kernel.org/r/20190912034421.GA2085@darwi-home-pc Link: https://lkml.kernel.org/r/20190911173624.GI2740@xxxxxxx Link: https://lkml.kernel.org/r/20180514003034.GI14763@xxxxxxxxx [ Modified by tytso@xxxxxxx to make the change of getrandom(2) to be non-blocking to be optional. ] Suggested-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Signed-off-by: Ahmed S. Darwish <darwish.07@xxxxxxxxx> Signed-off-by: Theodore Ts'o <tytso@xxxxxxx> --- Here's my take on the patch. I really very strongly believe that the idea of making getrandom(2) non-blocking and to blindly assume that we can load up the buffer with "best efforts" randomness to be a terrible, terrible idea that is going to cause major security problems that we will potentially regret very badly. Linus Torvalds believes I am an incompetent systems designer. So let's do it both ways, and push the decision on the distributor and/or product manufacturer drivers/char/Kconfig | 33 +++++++++++++++++++++++++++++++-- drivers/char/random.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3e866885a405..337baeca5ebc 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -557,8 +557,6 @@ config ADI and SSM (Silicon Secured Memory). Intended consumers of this driver include crash and makedumpfile. -endmenu - config RANDOM_TRUST_CPU bool "Trust the CPU manufacturer to initialize Linux's CRNG" depends on X86 || S390 || PPC @@ -573,3 +571,34 @@ config RANDOM_TRUST_CPU has not installed a hidden back door to compromise the CPU's random number generation facilities. This can also be configured at boot with "random.trust_cpu=on/off". + +config RANDOM_BLOCK + bool "Block if getrandom is called before CRNG is initialized" + help + Say Y here if you want userspace programs which call + getrandom(2) before the Cryptographic Random Number + Generator (CRNG) is initialized to block until + secure random numbers are available. + + Say N if you believe usability is more important than + security, so if getrandom(2) is called before the CRNG is + initialized, it should not block, but instead return "best + effort" randomness which might not be very secure or random + at all; but at least the system boot will not be delayed by + minutes or hours. + + This can also be controlled at boot with + "random.getrandom_block=on/off". + + Ideally, systems would be configured with hardware random + number generators, and/or configured to trust CPU-provided + RNG's. In addition, userspace should generate cryptographic + keys only as late as possible, when they are needed, instead + of during early boot. (For non-cryptographic use cases, + such as dictionary seeds or MIT Magic Cookies, other + mechanisms such as /dev/urandom or random(3) may be more + appropropriate.) This config option controls what the + kernel should do as a fallback when the non-ideal case + presents itself. + +endmenu diff --git a/drivers/char/random.c b/drivers/char/random.c index 5d5ea4ce1442..243fb4a4535f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -511,6 +511,8 @@ static struct ratelimit_state unseeded_warning = RATELIMIT_STATE_INIT("warn_unseeded_randomness", HZ, 3); static struct ratelimit_state urandom_warning = RATELIMIT_STATE_INIT("warn_urandom_randomness", HZ, 3); +static struct ratelimit_state getrandom_warning = + RATELIMIT_STATE_INIT("warn_getrandom_randomness", HZ, 3); static int ratelimit_disable __read_mostly; @@ -854,12 +856,19 @@ static void invalidate_batched_entropy(void); static void numa_crng_init(void); static bool trust_cpu __ro_after_init = IS_ENABLED(CONFIG_RANDOM_TRUST_CPU); +static bool getrandom_block __ro_after_init = IS_ENABLED(CONFIG_RANDOM_BLOCK); static int __init parse_trust_cpu(char *arg) { return kstrtobool(arg, &trust_cpu); } early_param("random.trust_cpu", parse_trust_cpu); +static int __init parse_block(char *arg) +{ + return kstrtobool(arg, &getrandom_block); +} +early_param("random.getrandom_block", parse_block); + static void crng_initialize(struct crng_state *crng) { int i; @@ -1045,6 +1054,12 @@ static void crng_reseed(struct crng_state *crng, struct entropy_store *r) urandom_warning.missed); urandom_warning.missed = 0; } + if (getrandom_warning.missed) { + pr_notice("random: %d getrandom warning(s) missed " + "due to ratelimiting\n", + getrandom_warning.missed); + getrandom_warning.missed = 0; + } } } @@ -1900,6 +1915,7 @@ int __init rand_initialize(void) crng_global_init_time = jiffies; if (ratelimit_disable) { urandom_warning.interval = 0; + getrandom_warning.interval = 0; unseeded_warning.interval = 0; } return 0; @@ -1969,8 +1985,8 @@ urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) if (!crng_ready() && maxwarn > 0) { maxwarn--; if (__ratelimit(&urandom_warning)) - printk(KERN_NOTICE "random: %s: uninitialized " - "urandom read (%zd bytes read)\n", + pr_err("random: %s: CRNG uninitialized " + "(%zd bytes read)\n", current->comm, nbytes); spin_lock_irqsave(&primary_crng.lock, flags); crng_init_cnt = 0; @@ -2135,9 +2151,17 @@ SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, if (!crng_ready()) { if (flags & GRND_NONBLOCK) return -EAGAIN; - ret = wait_for_random_bytes(); - if (unlikely(ret)) - return ret; + WARN_ON_ONCE(1); + if (getrandom_block) { + if (__ratelimit(&getrandom_warning)) + pr_err("random: %s: getrandom blocking for CRNG initialization\n", + current->comm); + ret = wait_for_random_bytes(); + if (unlikely(ret)) + return ret; + } else if (__ratelimit(&getrandom_warning)) + pr_err("random: %s: getrandom called too early\n", + current->comm); } return urandom_read(NULL, buf, count, NULL); } -- 2.23.0