]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
random-util: optionally enable blocking getrandom() behaviour
authorLennart Poettering <lennart@poettering.net>
Wed, 7 Nov 2018 18:04:04 +0000 (19:04 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Nov 2018 08:44:27 +0000 (09:44 +0100)
When generating the salt for the firstboot password logic, let's use
getrandom() blocking mode, and insist in the very best entropy.

src/basic/random-util.c
src/basic/random-util.h
src/firstboot/firstboot.c
src/test/test-random-util.c

index d7543d388a8711383a67225db2438b648e1ce4e2..1c54b07fbbcd1ad56e06ce13b56ba8fb40b5956e 100644 (file)
@@ -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) {
index 6328f661d20958e90fb36b331779f8fa661a2193..0813314a0557d6829f83ab562f01c044aa79e82f 100644 (file)
@@ -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 */
index ee267dcd7f1ce587854668a1225870311e163fd9..d8b5893f76f774c4c991a05b5fd6d995f86edd39 100644 (file)
@@ -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");
 
index adb2588f00bc6f966c6ce4c6c213ac80f28715d6..79c65a148ce3dd1b9eb6c832eb7f5e563b096694 100644 (file)
@@ -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();