about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2022-04-20 03:22:22 +0200
committerMichael Aldridge <aldridge.mac@gmail.com>2022-04-24 18:33:52 +0000
commit28f849335eed5db4787d3b7dc342dd4066b7183f (patch)
treed4216f8f5e0f264524c2583a91bdb968961d542b
parentf1a3950c9728dee890703c0a8a0cbd4328ba8035 (diff)
downloadrunit-void-28f849335eed5db4787d3b7dc342dd4066b7183f.tar.gz
runit-void-28f849335eed5db4787d3b7dc342dd4066b7183f.tar.xz
runit-void-28f849335eed5db4787d3b7dc342dd4066b7183f.zip
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.
-rw-r--r--seedrng.c193
1 files 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;
 }