From: Jason A. Donenfeld Date: Mon, 11 Apr 2022 22:53:49 +0000 (+0200) Subject: urandom-seed: go back to seeding with shell script temporarily X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=a001630a1aafa224f854fa3ea7589ecefb6e01ae;p=openwrt%2Fstaging%2Fsvanheule.git urandom-seed: go back to seeding with shell script temporarily This reverts commit 2edc017a6e0cb92b72b768aaa46c6d336ad84eff. We shouldn't be using a shell script here, but the SeedRNG integration into OpenWRT requires a bit more thought. Etienne raised some important points immediately after this was merged and planned to send some follow up commits, but became busy with other things. The points he raised are important enough that we should actually back this out until it's ready to go, and then merge it as a cohesive unit. So let's revert this for now, and come back to it later on. Cc: Etienne Champetier Cc: Petr Štetiar Signed-off-by: Jason A. Donenfeld --- diff --git a/package/system/urandom-seed/Makefile b/package/system/urandom-seed/Makefile index 0c8d77f445..7c5524a9db 100644 --- a/package/system/urandom-seed/Makefile +++ b/package/system/urandom-seed/Makefile @@ -9,6 +9,7 @@ include $(INCLUDE_DIR)/package.mk define Package/urandom-seed SECTION:=base CATEGORY:=Base system + DEPENDS:=+getrandom TITLE:=/etc/urandom.seed handling for OpenWrt URL:=https://openwrt.org/ endef @@ -18,15 +19,11 @@ define Build/Prepare endef define Build/Compile/Default - $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_CPPFLAGS) $(TARGET_LDFLAGS) \ - -std=gnu99 -o $(PKG_BUILD_DIR)/seedrng seedrng.c endef Build/Compile = $(Build/Compile/Default) define Package/urandom-seed/install $(CP) ./files/* $(1)/ - $(INSTALL_DIR) $(1)/sbin - $(CP) $(PKG_BUILD_DIR)/seedrng $(1)/sbin/ endef $(eval $(call BuildPackage,urandom-seed)) diff --git a/package/system/urandom-seed/files/etc/init.d/urandom_seed b/package/system/urandom-seed/files/etc/init.d/urandom_seed index d6e81c6079..17d9c13400 100755 --- a/package/system/urandom-seed/files/etc/init.d/urandom_seed +++ b/package/system/urandom-seed/files/etc/init.d/urandom_seed @@ -5,7 +5,7 @@ USE_PROCD=1 start_service() { procd_open_instance "urandom_seed" - procd_set_param command "/sbin/seedrng" + procd_set_param command "/sbin/urandom_seed" procd_set_param stdout 1 procd_set_param stderr 1 procd_close_instance diff --git a/package/system/urandom-seed/files/lib/preinit/81_urandom_seed b/package/system/urandom-seed/files/lib/preinit/81_urandom_seed index b3014daeaf..2adc6c47f0 100644 --- a/package/system/urandom-seed/files/lib/preinit/81_urandom_seed +++ b/package/system/urandom-seed/files/lib/preinit/81_urandom_seed @@ -2,11 +2,21 @@ log_urandom_seed() { echo "urandom-seed: $1" > /dev/kmsg } +_do_urandom_seed() { + [ -f "$1" ] || { log_urandom_seed "Seed file not found ($1)"; return; } + [ -O "$1" -a -G "$1" -a ! -x "$1" ] || { log_urandom_seed "Wrong owner / permissions for $1"; return; } + + log_urandom_seed "Seeding with $1" + cat "$1" > /dev/urandom +} + do_urandom_seed() { [ -c /dev/urandom ] || { log_urandom_seed "Something is wrong with /dev/urandom"; return; } - seedrng 2>&1 | while read -r line; do - log_urandom_seed "$line" - done + + _do_urandom_seed "/etc/urandom.seed" + + SEED="$(uci -q get system.@system[0].urandom_seed)" + [ "${SEED:0:1}" = "/" -a "$SEED" != "/etc/urandom.seed" ] && _do_urandom_seed "$SEED" } boot_hook_add preinit_main do_urandom_seed diff --git a/package/system/urandom-seed/files/sbin/urandom_seed b/package/system/urandom-seed/files/sbin/urandom_seed new file mode 100755 index 0000000000..7043e8af4e --- /dev/null +++ b/package/system/urandom-seed/files/sbin/urandom_seed @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +trap '[ "$?" -eq 0 ] || echo "An error occured" >&2' EXIT + +save() { + touch "$1.tmp" + chown root:root "$1.tmp" + chmod 600 "$1.tmp" + getrandom 512 > "$1.tmp" + mv "$1.tmp" "$1" + echo "Seed saved ($1)" +} + +SEED="$(uci -q get system.@system[0].urandom_seed || true)" +[ "${SEED:0:1}" = "/" ] && save "$SEED" + +SEED=/etc/urandom.seed +[ ! -f $SEED ] && save "$SEED" +true diff --git a/package/system/urandom-seed/seedrng.c b/package/system/urandom-seed/seedrng.c deleted file mode 100644 index 9a2cb10f55..0000000000 --- a/package/system/urandom-seed/seedrng.c +++ /dev/null @@ -1,434 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0) -/* - * Copyright (C) 2022 Jason A. Donenfeld . All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SEED_DIR "/etc/seedrng" -#define CREDITABLE_SEED SEED_DIR "/seed.credit" -#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit" -#define LOCK_FILE "/tmp/run/seedrng.lock" - -enum blake2s_lengths { - BLAKE2S_BLOCK_LEN = 64, - BLAKE2S_HASH_LEN = 32, - BLAKE2S_KEY_LEN = 32 -}; - -enum seedrng_lengths { - MAX_SEED_LEN = 512, - MIN_SEED_LEN = BLAKE2S_HASH_LEN -}; - -struct blake2s_state { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCK_LEN]; - unsigned int buflen; - unsigned int outlen; -}; - -#define le32_to_cpup(a) le32toh(*(a)) -#define cpu_to_le32(a) htole32(a) -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#endif - -static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = cpu_to_le32(*buf); - ++buf; - } -} - -static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = le32_to_cpup(buf); - ++buf; - } -} - -static inline uint32_t ror32(uint32_t word, unsigned int shift) -{ - return (word >> (shift & 31)) | (word << ((-shift) & 31)); -} - -static const uint32_t blake2s_iv[8] = { - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL -}; - -static const uint8_t blake2s_sigma[10][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, -}; - -static void blake2s_set_lastblock(struct blake2s_state *state) -{ - state->f[0] = -1; -} - -static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc) -{ - state->t[0] += inc; - state->t[1] += (state->t[0] < inc); -} - -static void blake2s_init_param(struct blake2s_state *state, const uint32_t param) -{ - int i; - - memset(state, 0, sizeof(*state)); - for (i = 0; i < 8; ++i) - state->h[i] = blake2s_iv[i]; - state->h[0] ^= param; -} - -static void blake2s_init(struct blake2s_state *state, const size_t outlen) -{ - blake2s_init_param(state, 0x01010000 | outlen); - state->outlen = outlen; -} - -static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc) -{ - uint32_t m[16]; - uint32_t v[16]; - int i; - - while (nblocks > 0) { - blake2s_increment_counter(state, inc); - memcpy(m, block, BLAKE2S_BLOCK_LEN); - le32_to_cpu_array(m, ARRAY_SIZE(m)); - memcpy(v, state->h, 32); - v[ 8] = blake2s_iv[0]; - v[ 9] = blake2s_iv[1]; - v[10] = blake2s_iv[2]; - v[11] = blake2s_iv[3]; - v[12] = blake2s_iv[4] ^ state->t[0]; - v[13] = blake2s_iv[5] ^ state->t[1]; - v[14] = blake2s_iv[6] ^ state->f[0]; - v[15] = blake2s_iv[7] ^ state->f[1]; - -#define G(r, i, a, b, c, d) do { \ - a += b + m[blake2s_sigma[r][2 * i + 0]]; \ - d = ror32(d ^ a, 16); \ - c += d; \ - b = ror32(b ^ c, 12); \ - a += b + m[blake2s_sigma[r][2 * i + 1]]; \ - d = ror32(d ^ a, 8); \ - c += d; \ - b = ror32(b ^ c, 7); \ -} while (0) - -#define ROUND(r) do { \ - G(r, 0, v[0], v[ 4], v[ 8], v[12]); \ - G(r, 1, v[1], v[ 5], v[ 9], v[13]); \ - G(r, 2, v[2], v[ 6], v[10], v[14]); \ - G(r, 3, v[3], v[ 7], v[11], v[15]); \ - G(r, 4, v[0], v[ 5], v[10], v[15]); \ - G(r, 5, v[1], v[ 6], v[11], v[12]); \ - G(r, 6, v[2], v[ 7], v[ 8], v[13]); \ - G(r, 7, v[3], v[ 4], v[ 9], v[14]); \ -} while (0) - ROUND(0); - ROUND(1); - ROUND(2); - ROUND(3); - ROUND(4); - ROUND(5); - ROUND(6); - ROUND(7); - ROUND(8); - ROUND(9); - -#undef G -#undef ROUND - - for (i = 0; i < 8; ++i) - state->h[i] ^= v[i] ^ v[i + 8]; - - block += BLAKE2S_BLOCK_LEN; - --nblocks; - } -} - -static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen) -{ - const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen; - const uint8_t *in = inp; - - if (!inlen) - return; - if (inlen > fill) { - memcpy(state->buf + state->buflen, in, fill); - blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN); - state->buflen = 0; - in += fill; - inlen -= fill; - } - if (inlen > BLAKE2S_BLOCK_LEN) { - const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN); - blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN); - in += BLAKE2S_BLOCK_LEN * (nblocks - 1); - inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1); - } - memcpy(state->buf + state->buflen, in, inlen); - state->buflen += inlen; -} - -static void blake2s_final(struct blake2s_state *state, uint8_t *out) -{ - blake2s_set_lastblock(state); - memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen); - blake2s_compress(state, state->buf, 1, state->buflen); - cpu_to_le32_array(state->h, ARRAY_SIZE(state->h)); - memcpy(out, state->h, state->outlen); -} - -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)); - ret = MIN_SEED_LEN; - } else - ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8); - if (fd >= 0) - close(fd); - if (ret < MIN_SEED_LEN) - ret = MIN_SEED_LEN; - else if (ret > MAX_SEED_LEN) - ret = MAX_SEED_LEN; - return ret; -} - -static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) -{ - ssize_t ret; - int urandom_fd; - - *is_creditable = false; - ret = getrandom(seed, len, GRND_NONBLOCK); - if (ret == (ssize_t)len) { - *is_creditable = true; - return 0; - } else if (ret < 0 && errno == ENOSYS) { - struct pollfd random_fd = { - .fd = open("/dev/random", O_RDONLY), - .events = POLLIN - }; - if (random_fd.fd < 0) - return -errno; - *is_creditable = poll(&random_fd, 1, 0) == 1; - close(random_fd.fd); - } else if (getrandom(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); - if (ret == (ssize_t)len) - ret = 0; - else - ret = -errno ? -errno : -EIO; - close(urandom_fd); - return ret; -} - -static int seed_rng(uint8_t *seed, size_t len, bool credit) -{ - struct { - int entropy_count; - int buf_size; - uint8_t buffer[MAX_SEED_LEN]; - } req = { - .entropy_count = credit ? len * 8 : 0, - .buf_size = len - }; - int random_fd, ret; - - if (len > sizeof(req.buffer)) - return -EFBIG; - memcpy(req.buffer, seed, len); - - random_fd = open("/dev/random", O_RDWR); - if (random_fd < 0) - return -errno; - ret = ioctl(random_fd, RNDADDENTROPY, &req); - if (ret) - ret = -errno ? -errno : -EIO; - close(random_fd); - return ret; -} - -static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash) -{ - uint8_t seed[MAX_SEED_LEN]; - ssize_t seed_len; - int fd, dfd, ret = 0; - - fd = open(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; - } - seed_len = read(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; - } - if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - ret = -errno; - fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno)); - } - close(dfd); - if (ret) - return ret; - if (!seed_len) - return 0; - - 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; -} - -static bool skip_credit(void) -{ - const char *skip = getenv("SEEDRNG_SKIP_CREDIT"); - return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") || - !strcasecmp(skip, "yes") || !strcasecmp(skip, "y")); -} - -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; - uint8_t new_seed[MAX_SEED_LEN]; - size_t new_seed_len; - bool new_seed_creditable; - struct timespec realtime = { 0 }, boottime = { 0 }; - struct blake2s_state hash; - - umask(0077); - if (getuid()) { - fprintf(stderr, "ERROR: This program requires root\n"); - return 1; - } - - blake2s_init(&hash, BLAKE2S_HASH_LEN); - blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix)); - clock_gettime(CLOCK_REALTIME, &realtime); - clock_gettime(CLOCK_BOOTTIME, &boottime); - blake2s_update(&hash, &realtime, sizeof(realtime)); - 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)); - 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)); - program_ret = 1; - goto out; - } - - ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash); - if (ret < 0) - program_ret |= 1 << 1; - ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash); - if (ret < 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)); - new_seed_len = BLAKE2S_HASH_LEN; - strncpy((char *)new_seed, seedrng_failure, new_seed_len); - program_ret |= 1 << 3; - } - blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len)); - 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); - if (fd < 0) { - fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno)); - 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)); - 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)); - program_ret |= 1 << 6; - } -out: - if (fd >= 0) - close(fd); - if (lock >= 0) - close(lock); - return program_ret; -}