From: Lennart Poettering Date: Wed, 7 Nov 2018 18:04:04 +0000 (+0100) Subject: random-util: optionally enable blocking getrandom() behaviour X-Git-Tag: v240~380^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68534345b8af31e8df3d45cf21a832a42af52996;p=thirdparty%2Fsystemd.git random-util: optionally enable blocking getrandom() behaviour When generating the salt for the firstboot password logic, let's use getrandom() blocking mode, and insist in the very best entropy. --- diff --git a/src/basic/random-util.c b/src/basic/random-util.c index d7543d388a8..1c54b07fbbc 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -69,74 +69,94 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { static int have_syscall = -1; _cleanup_close_ int fd = -1; - size_t already_done = 0; int r; - /* Gathers some randomness from the kernel. This call will never block. If RANDOM_EXTEND_WITH_PSEUDO is unset, - * it will always return some data from the kernel, regardless of whether the random pool is fully initialized - * or not. Otherwise, it will return success if at least some random bytes were successfully acquired, and an - * error if the kernel has no entropy whatsover for us. */ + /* Gathers some randomness from the kernel. This call won't block, unless the RANDOM_BLOCK flag is set. If + * RANDOM_EXTEND_WITH_PSEUDO is unset, it will always return some data from the kernel, regardless of whether + * the random pool is fully initialized or not. Otherwise, it will return success if at least some random + * bytes were successfully acquired, and an error if the kernel has no entropy whatsover for us. */ /* Use the getrandom() syscall unless we know we don't have it. */ if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - r = getrandom(p, n, GRND_NONBLOCK); - if (r > 0) { - have_syscall = true; - if ((size_t) r == n) - return 0; - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudo_random_bytes((uint8_t*) p + r, n - r); - return 0; - } - - already_done = r; - } else if (r == 0) { - have_syscall = true; - return -EIO; - } else if (errno == ENOSYS) - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to - * use the syscall the next time again though. - * - * If high_quality_required is false, return an error so that - * random_bytes() can produce some pseudorandom - * bytes. Otherwise, fall back to /dev/urandom, which we know - * is empty, but the kernel will produce some bytes for us on - * a best-effort basis. */ - have_syscall = true; - - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - uint64_t u; - size_t k; - - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality - * randomness is not required, as we don't trust it (who does?). Note that we only do a - * single iteration of RDRAND here, even though the Intel docs suggest calling this in - * a tight loop of 10 invocatins or so. That's because we don't really care about the - * quality here. */ - - if (rdrand64(&u) < 0) - return -ENODATA; - - k = MIN(n, sizeof(u)); - memcpy(p, &u, k); - - /* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */ - pseudo_random_bytes((uint8_t*) p + k, n - k); - return 0; - } - } else - return -errno; + + for (;;) { + r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK); + if (r > 0) { + have_syscall = true; + + if ((size_t) r == n) + return 0; /* Yay, success! */ + + assert((size_t) r < n); + p = (uint8_t*) p + r; + n -= r; + + if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { + /* Fill in the remaining bytes using pseudo-random values */ + pseudo_random_bytes(p, n); + return 0; + } + + /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */ + if (FLAGS_SET(flags, RANDOM_BLOCK)) + continue; + + /* Fill in the rest with /dev/urandom */ + break; + + } else if (r == 0) { + have_syscall = true; + return -EIO; + + } else if (errno == ENOSYS) { + /* We lack the syscall, continue with reading from /dev/urandom. */ + have_syscall = false; + break; + + } else if (errno == EAGAIN) { + /* The kernel has no entropy whatsoever. Let's remember to + * use the syscall the next time again though. + * + * If high_quality_required is false, return an error so that + * random_bytes() can produce some pseudorandom + * bytes. Otherwise, fall back to /dev/urandom, which we know + * is empty, but the kernel will produce some bytes for us on + * a best-effort basis. */ + have_syscall = true; + + if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { + uint64_t u; + size_t k; + + /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality + * randomness is not required, as we don't trust it (who does?). Note that we only do a + * single iteration of RDRAND here, even though the Intel docs suggest calling this in + * a tight loop of 10 invocatins or so. That's because we don't really care about the + * quality here. */ + + if (rdrand64(&u) < 0) + return -ENODATA; + + k = MIN(n, sizeof(u)); + memcpy(p, &u, k); + + /* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */ + pseudo_random_bytes((uint8_t*) p + k, n - k); + return 0; + } + + /* Use /dev/urandom instead */ + break; + } else + return -errno; + } } fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return errno == ENOENT ? -ENOSYS : -errno; - return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); + return loop_read_exact(fd, p, n, true); } void initialize_srand(void) { diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 6328f661d20..0813314a055 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -7,6 +7,7 @@ typedef enum RandomFlags { RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ + RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */ diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index ee267dcd7f1..d8b5893f76f 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -647,7 +647,8 @@ static int process_root_password(void) { if (!arg_root_password) return 0; - r = genuine_random_bytes(raw, 16, 0); + /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */ + r = genuine_random_bytes(raw, 16, RANDOM_BLOCK); if (r < 0) return log_error_errno(r, "Failed to get salt: %m"); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c index adb2588f00b..79c65a148ce 100644 --- a/src/test/test-random-util.c +++ b/src/test/test-random-util.c @@ -56,6 +56,7 @@ int main(int argc, char **argv) { test_genuine_random_bytes(RANDOM_EXTEND_WITH_PSEUDO); test_genuine_random_bytes(0); + test_genuine_random_bytes(RANDOM_BLOCK); test_pseudo_random_bytes();