From 28f849335eed5db4787d3b7dc342dd4066b7183f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 20 Apr 2022 03:22:22 +0200 Subject: seedrng: incorporate various improvements There have been a number of subtle improvements and cleanups to seedrng, including using openat and locking the directory fd instead of a separate lock file. Also various stylistic cleanups. --- seedrng.c | 193 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 73 deletions(-) diff --git a/seedrng.c b/seedrng.c index 2bc7521..59e1a21 100644 --- a/seedrng.c +++ b/seedrng.c @@ -21,14 +21,10 @@ #ifndef LOCALSTATEDIR #define LOCALSTATEDIR "/var/lib" #endif -#ifndef RUNSTATEDIR -#define RUNSTATEDIR "/run" -#endif #define SEED_DIR LOCALSTATEDIR "/seedrng" -#define CREDITABLE_SEED SEED_DIR "/seed.credit" -#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit" -#define LOCK_FILE RUNSTATEDIR "/seedrng.lock" +#define CREDITABLE_SEED "seed.credit" +#define NON_CREDITABLE_SEED "seed.no-credit" enum blake2s_lengths { BLAKE2S_BLOCK_LEN = 64, @@ -61,7 +57,7 @@ struct blake2s_state { static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) { - while (words--) { + while (words--) { *buf = cpu_to_le32(*buf); ++buf; } @@ -69,10 +65,10 @@ static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words) { - while (words--) { + while (words--) { *buf = le32_to_cpup(buf); ++buf; - } + } } static inline uint32_t ror32(uint32_t word, unsigned int shift) @@ -221,14 +217,70 @@ static void blake2s_final(struct blake2s_state *state, uint8_t *out) memcpy(out, state->h, state->outlen); } +static ssize_t getrandom_full(void *buf, size_t count, unsigned int flags) +{ + ssize_t ret, total = 0; + uint8_t *p = buf; + + do { + ret = getrandom(p, count, flags); + if (ret < 0 && errno == EINTR) + continue; + else if (ret < 0) + return ret; + total += ret; + p += ret; + count -= ret; + } while (count); + return total; +} + +static ssize_t read_full(int fd, void *buf, size_t count) +{ + ssize_t ret, total = 0; + uint8_t *p = buf; + + do { + ret = read(fd, p, count); + if (ret < 0 && errno == EINTR) + continue; + else if (ret < 0) + return ret; + else if (ret == 0) + break; + total += ret; + p += ret; + count -= ret; + } while (count); + return total; +} + +static ssize_t write_full(int fd, const void *buf, size_t count) +{ + ssize_t ret, total = 0; + const uint8_t *p = buf; + + do { + ret = write(fd, p, count); + if (ret < 0 && errno == EINTR) + continue; + else if (ret < 0) + return ret; + total += ret; + p += ret; + count -= ret; + } while (count); + return total; +} + static size_t determine_optimal_seed_len(void) { size_t ret = 0; char poolsize_str[11] = { 0 }; int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY); - if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) { - fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno)); + if (fd < 0 || read_full(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) { + perror("Unable to determine pool size, falling back to 256 bits"); ret = MIN_SEED_LEN; } else ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8); @@ -247,7 +299,7 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) int urandom_fd; *is_creditable = false; - ret = getrandom(seed, len, GRND_NONBLOCK); + ret = getrandom_full(seed, len, GRND_NONBLOCK); if (ret == (ssize_t)len) { *is_creditable = true; return 0; @@ -260,18 +312,19 @@ static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) return -errno; *is_creditable = poll(&random_fd, 1, 0) == 1; close(random_fd.fd); - } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) + } else if (getrandom_full(seed, len, GRND_INSECURE) == (ssize_t)len) return 0; urandom_fd = open("/dev/urandom", O_RDONLY); if (urandom_fd < 0) - return -errno; - ret = read(urandom_fd, seed, len); + return -1; + ret = read_full(urandom_fd, seed, len); if (ret == (ssize_t)len) ret = 0; else ret = -errno ? -errno : -EIO; close(urandom_fd); - return ret; + errno = -ret; + return ret ? -1 : 0; } static int seed_rng(uint8_t *seed, size_t len, bool credit) @@ -286,69 +339,65 @@ static int seed_rng(uint8_t *seed, size_t len, bool credit) }; int random_fd, ret; - if (len > sizeof(req.buffer)) - return -EFBIG; + if (len > sizeof(req.buffer)) { + errno = EFBIG; + return -1; + } memcpy(req.buffer, seed, len); - random_fd = open("/dev/random", O_RDWR); + random_fd = open("/dev/urandom", O_RDONLY); if (random_fd < 0) - return -errno; + return -1; ret = ioctl(random_fd, RNDADDENTROPY, &req); if (ret) ret = -errno ? -errno : -EIO; close(random_fd); - return ret; + errno = -ret; + return ret ? -1 : 0; } -static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash) +static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, struct blake2s_state *hash) { uint8_t seed[MAX_SEED_LEN]; ssize_t seed_len; - int fd, dfd, ret = 0; + int fd = -1, ret = 0; - fd = open(filename, O_RDONLY); + fd = openat(dfd, filename, O_RDONLY); if (fd < 0 && errno == ENOENT) return 0; else if (fd < 0) { ret = -errno; - fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno)); - return ret; - } - dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY); - if (dfd < 0) { - ret = -errno; - close(fd); - fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno)); - return ret; + perror("Unable to open seed file"); + goto out; } - seed_len = read(fd, seed, sizeof(seed)); + seed_len = read_full(fd, seed, sizeof(seed)); if (seed_len < 0) { ret = -errno; - fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno)); - } - close(fd); - if (ret) { - close(dfd); - return ret; + perror("Unable to read seed file"); + goto out; } - if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { + if ((unlinkat(dfd, filename, 0) < 0 || fsync(dfd) < 0) && seed_len) { ret = -errno; - fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno)); + perror("Unable to remove seed after reading, so not seeding"); + goto out; } - close(dfd); - if (ret) - return ret; if (!seed_len) - return 0; + goto out; blake2s_update(hash, &seed_len, sizeof(seed_len)); blake2s_update(hash, seed, seed_len); - fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); - ret = seed_rng(seed, seed_len, credit); - if (ret < 0) - fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret)); - return ret; + printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without"); + if (seed_rng(seed, seed_len, credit) < 0) { + ret = -errno; + perror("Unable to seed"); + } + +out: + if (fd >= 0) + close(fd); + errno = -ret; + return ret ? -1 : 0; } static bool skip_credit(void) @@ -362,7 +411,7 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) { static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; - int ret, fd = -1, lock, program_ret = 0; + int fd = -1, dfd = -1, program_ret = 0; uint8_t new_seed[MAX_SEED_LEN]; size_t new_seed_len; bool new_seed_creditable; @@ -371,7 +420,8 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) umask(0077); if (getuid()) { - fprintf(stderr, "ERROR: This program requires root\n"); + errno = EACCES; + perror("This program requires root"); return 1; } @@ -383,28 +433,25 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) blake2s_update(&hash, &boottime, sizeof(boottime)); if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) { - fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno)); + perror("Unable to create seed directory"); return 1; } - lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000); - if (lock < 0 || flock(lock, LOCK_EX) < 0) { - fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno)); + dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY); + if (dfd < 0 || flock(dfd, LOCK_EX) < 0) { + perror("Unable to lock seed directory"); program_ret = 1; goto out; } - ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash); - if (ret < 0) + if (seed_from_file_if_exists(NON_CREDITABLE_SEED, dfd, false, &hash) < 0) program_ret |= 1 << 1; - ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash); - if (ret < 0) + if (seed_from_file_if_exists(CREDITABLE_SEED, dfd, !skip_credit(), &hash) < 0) program_ret |= 1 << 2; new_seed_len = determine_optimal_seed_len(); - ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); - if (ret < 0) { - fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret)); + if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) { + perror("Unable to read new seed"); new_seed_len = BLAKE2S_HASH_LEN; strncpy((char *)new_seed, seedrng_failure, new_seed_len); program_ret |= 1 << 3; @@ -413,26 +460,26 @@ int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) blake2s_update(&hash, new_seed, new_seed_len); blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN); - fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); - fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400); + printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); + fd = openat(dfd, NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400); if (fd < 0) { - fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno)); + perror("Unable to open seed file for writing"); program_ret |= 1 << 4; goto out; } - if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { - fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno)); + if (write_full(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { + perror("Unable to write seed file"); program_ret |= 1 << 5; goto out; } - if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) { - fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno)); + if (new_seed_creditable && renameat(dfd, NON_CREDITABLE_SEED, dfd, CREDITABLE_SEED) < 0) { + perror("Unable to make new seed creditable"); program_ret |= 1 << 6; } out: if (fd >= 0) close(fd); - if (lock >= 0) - close(lock); + if (dfd >= 0) + close(dfd); return program_ret; } -- cgit 1.4.1