diff --git a/src/basic/random-util.c b/src/basic/random-util.c index b2ca2cc8e3..490107ef41 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -42,53 +42,59 @@ #include "random-util.h" #include "time-util.h" -int dev_urandom(void *p, size_t n) { +int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { static int have_syscall = -1; _cleanup_close_ int fd = -1; int r; + unsigned already_done = 0; - /* Gathers some randomness from the kernel. This call will - * never block, and will always return some data from the - * kernel, regardless if the random pool is fully initialized - * or not. It thus makes no guarantee for the quality of the - * returned entropy, but is good enough for our usual usecases - * of seeding the hash functions for hashtable */ + /* Gathers some randomness from the kernel. This call will never block. If + * high_quality_required, 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, or when the requested size is too large for it. */ - if (have_syscall != 0 || (size_t) (int) n != n) { + /* Use the getrandom() syscall unless we know we don't have it. */ + if (have_syscall != 0) { r = getrandom(p, n, GRND_NONBLOCK); - if (r == (int) n) { + if (r > 0) { have_syscall = true; - return 0; - } + if (r == n) + return 0; + if (!high_quality_required) { + /* Fill in the remaing bytes using pseudorandom values */ + pseudorandom_bytes((uint8_t*) p + r, n - r); + return 0; + } - if (r < 0) { - if (errno == ENOSYS) - /* we lack the syscall, continue with - * reading from /dev/urandom */ - have_syscall = false; - else if (errno == EAGAIN) - /* not enough entropy for now. Let's - * remember to use the syscall the - * next time, again, but also read - * from /dev/urandom for now, which - * doesn't care about the current - * amount of entropy. */ - have_syscall = true; - else - return -errno; + already_done = r; + } 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 (!high_quality_required) + return -ENODATA; } else - /* too short read? */ - return -ENODATA; + 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, p, n, true); + return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); } void initialize_srand(void) { @@ -102,8 +108,9 @@ void initialize_srand(void) { return; #ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than nothing... */ + /* The kernel provides us with 16 bytes of entropy in auxv, so let's + * try to make use of that to seed the pseudo-random generator. It's + * better than nothing... */ auxv = (void*) getauxval(AT_RANDOM); if (auxv) { @@ -154,7 +161,7 @@ void pseudorandom_bytes(void *p, size_t n) { void random_bytes(void *p, size_t n) { int r; - r = dev_urandom(p, n); + r = acquire_random_bytes(p, n, false); if (r >= 0) return; diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 294ef4d4e5..804e225fc1 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -19,10 +19,11 @@ along with systemd; If not, see . ***/ +#include #include #include -int dev_urandom(void *p, size_t n); +int acquire_random_bytes(void *p, size_t n, bool high_quality_required); void pseudorandom_bytes(void *p, size_t n); void random_bytes(void *p, size_t n); void initialize_srand(void); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index bc16290c72..b3578d3e16 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -572,7 +572,7 @@ static int process_root_password(void) { if (!arg_root_password) return 0; - r = dev_urandom(raw, 16); + r = acquire_random_bytes(raw, 16, true); if (r < 0) return log_error_errno(r, "Failed to get salt: %m"); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index cc89f2de2e..b1f6c5305f 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -289,7 +289,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - r = dev_urandom(&t, sizeof(t)); + r = acquire_random_bytes(&t, sizeof t, true); if (r < 0) return r;