diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2022-06-08 17:53:50 +0000 |
---|---|---|
committer | Laurent Bercot <ska@appnovation.com> | 2022-06-08 17:53:50 +0000 |
commit | ad5973028c42d947440cdae5e4f106152c3dda28 (patch) | |
tree | e3fc1cb6767c5436ee269fedd80d0d063ea98bc5 /src/minutils/rngseed.c | |
parent | 8add2dbec0842d5b051b4dba5536fba9d82ce6bc (diff) | |
download | s6-linux-utils-ad5973028c42d947440cdae5e4f106152c3dda28.tar.gz s6-linux-utils-ad5973028c42d947440cdae5e4f106152c3dda28.tar.xz s6-linux-utils-ad5973028c42d947440cdae5e4f106152c3dda28.zip |
Prepare for 2.6.0.0; delete s6-fillurandompool; add rngseed
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src/minutils/rngseed.c')
-rw-r--r-- | src/minutils/rngseed.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/minutils/rngseed.c b/src/minutils/rngseed.c new file mode 100644 index 0000000..f00c4fc --- /dev/null +++ b/src/minutils/rngseed.c @@ -0,0 +1,279 @@ +/* ISC license. */ + +#include <skalibs/sysdeps.h> + +#ifndef SKALIBS_HASCLOCKBOOT +# error "CLOCK_BOOTTIME required" +#endif + +#include <string.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/random.h> +#include <linux/random.h> + +#include <skalibs/types.h> +#include <skalibs/sgetopt.h> +#include <skalibs/error.h> +#include <skalibs/strerr2.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/djbunix.h> +#include <skalibs/tai.h> +#include <skalibs/iopause.h> +#include <skalibs/blake2s.h> +#include <skalibs/random.h> + +#include <s6-linux-utils/config.h> + +#define USAGE "rngseed [ -d seeddir ] [ -v verbosity ] [ -r | -R ] [ -n | -N ] [ -w | -W ]" +#define dieusage() strerr_dieusage(100, USAGE) + +#define HASH_PREFIX "SeedRNG v1 Old+New Prefix" +#define HASH_FALLBACK "SeedRNG v1 No New Seed Failure" + +struct flags_s +{ + unsigned int read: 1 ; + unsigned int rcred: 1 ; + unsigned int block: 1 ; + unsigned int write: 1 ; + unsigned int wcred: 1 ; +} ; +#define FLAGS_ZERO { .read = 0, .rcred = 1, .block = 1, .write = 0, .wcred = 1 } + +struct randpoolinfo_s +{ + int entropy_count ; + int buf_size ; + char buffer[512] ; +} ; + +static unsigned int verbosity = 1 ; + +static inline void mkdirp (char *s, size_t len) +{ + mode_t m = umask(0) ; + size_t i = 1 ; + for (; i < len ; i++) if (s[i] == '/') + { + s[i] = 0 ; + if (mkdir(s, 02755) < 0 && errno != EEXIST) + strerr_diefu2sys(111, "mkdir ", s) ; + s[i] = '/' ; + } + umask(m) ; +} + +static inline int read_seed_nb (char *s, size_t len) +{ + int wcred ; + size_t w = 0 ; +#ifdef SKALIBS_HASGETRANDOM + while (w < len) + { + ssize_t r = getrandom(s + w, len - w, GRND_NONBLOCK) ; + if (r == -1) + { + if (errno == EINTR) continue ; + if (error_isagain(errno)) break ; + strerr_diefu1sys(111, "getrandom") ; + } + else w += r ; + } + wcred = w >= len ; + if (!wcred) +#else + tain dummy = TAIN_EPOCH ; + iopause_fd x = { .events = IOPAUSE_READ } ; + x.fd = openbc_read("/dev/random") ; + if (x.fd == -1) strerr_diefu2sys(111, "open ", "/dev/random") ; + wcred = iopause(&x, 1, &dummy, &dummy) ; + if (wcred == -1) strerr_diefu1sys(111, "iopause") ; + fd_close(x.fd) ; +#endif + random_devurandom(s + w, len - w) ; + return wcred ; +} + +int main (int argc, char const *const *argv) +{ + blake2s_ctx ctx = BLAKE2S_INIT(32) ; + char const *seeddir = RNGSEED_DIR ; + struct flags_s flags = FLAGS_ZERO ; + PROG = "rngseed" ; + { + subgetopt l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "d:v:rRnNwW", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : seeddir = l.arg ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'r' : flags.read = 1 ; flags.rcred = 1 ; break ; + case 'R' : flags.read = 1 ; flags.rcred = 0 ; break ; + case 'n' : flags.block = 0 ; break ; + case 'N' : flags.block = 1 ; break ; + case 'w' : flags.write = 1 ; flags.wcred = 1 ; break ; + case 'W' : flags.write = 1 ; flags.wcred = 0 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + { + size_t dirlen = strlen(seeddir) ; + char file[dirlen + 6] ; + memcpy(file, seeddir, dirlen) ; + while (dirlen && file[dirlen-1] == '/') dirlen-- ; + memcpy(file + dirlen, "/seed", 6) ; + + if (flags.write) + { + struct timespec ts ; + if (dirlen) + { + file[dirlen] = 0 ; + mkdirp(file, dirlen) ; + if (mkdir(file, 0700) == -1) + { + struct stat st ; + if (errno != EEXIST) strerr_diefu2sys(111, "mkdir ", file) ; + if (stat(file, &st) == -1) + strerr_diefu2sys(111, "stat ", file) ; + if (st.st_mode & 0077) + { + if (verbosity) + strerr_warnw2sys(file, "has permissive modes, changing it to 0700") ; + if (chmod(file, 0700) == -1) + strerr_diefu2sys(111, "chmod ", file) ; + } + } + file[dirlen] = '/' ; + } + blake2s_update(&ctx, HASH_PREFIX, sizeof(HASH_PREFIX) - 1) ; + clock_gettime(CLOCK_REALTIME, &ts) ; + blake2s_update(&ctx, (char *)&ts, sizeof ts) ; + clock_gettime(CLOCK_BOOTTIME, &ts) ; + blake2s_update(&ctx, (char *)&ts, sizeof ts) ; + } + + if (flags.read) + { + struct randpoolinfo_s req ; + struct stat st ; + size_t seedlen ; + int fd ; + if (verbosity >= 2) strerr_warni2x("reading seed from ", file) ; + fd = openbc_read(file) ; + if (fd == -1) strerr_diefu2sys(111, "open ", file) ; + errno = 0 ; + seedlen = allread(fd, req.buffer, 512) ; + if (errno) strerr_diefu2sys(111, "read from ", file) ; + if (!seedlen) strerr_dief2x(100, "empty ", file) ; + if (fstat(fd, &st) == -1) strerr_diefu2sys(111, "stat ", file) ; + if (unlink(file) == -1) strerr_diefu2sys(111, "unlink ", file) ; + fd_close(fd) ; + if (flags.write) + { + blake2s_update(&ctx, (char *)&seedlen, sizeof(seedlen)) ; + blake2s_update(&ctx, req.buffer, seedlen) ; + } + if (flags.rcred && st.st_mode & S_IWUSR && verbosity) + strerr_warnw2x(file, " was not marked as creditable") ; + req.entropy_count = flags.rcred && !(st.st_mode & S_IWUSR) ? (seedlen << 3) : 0 ; + req.buf_size = seedlen ; + fd = openbc_read("/dev/urandom") ; + if (fd == -1) strerr_diefu2sys(111, "open ", "/dev/urandom") ; + if (verbosity >= 2) + { + char fmt[SIZE_FMT] ; + fmt[size_fmt(fmt, seedlen << 3)] = 0 ; + strerr_warni4x("seeding with ", fmt, " bits", req.entropy_count ? "" : " without crediting") ; + } + if (ioctl(fd, RNDADDENTROPY, &req) == -1) + strerr_diefu1sys(111, "seed") ; + fd_close(fd) ; + } + + if (flags.block) + { + if (verbosity >= 2) + strerr_warni1x("waiting for the entropy pool to initialize") ; +#ifdef SKALIBS_HASGETRANDOM + char c ; + ssize_t r = getrandom(&c, 1, 0) ; + if (r == -1) strerr_diefu1sys(111, "getrandom") ; +#else + iopause_fd x = { .events = IOPAUSE_READ } ; + x.fd = openbc_read("/dev/random") ; + if (x.fd == -1) strerr_diefu2sys(111, "open ", "/dev/random") ; + if (iopause(&x, 1, 0, 0) == -1) strerr_diefu1sys(111, "iopause") ; + fd_close(x.fd) ; +#endif + } + + if (flags.write) + { + char seed[512] ; + size_t len = 512 ; + int wcred = 1 ; + char s[SIZE_FMT] = "" ; + int fd = openbc_read("/proc/sys/kernel/random/poolsize") ; + if (fd < 0) + { + if (verbosity) strerr_warnwu2sys("open ", "/proc/sys/kernel/random/poolsize") ; + } + else + { + size_t r ; + errno = 0 ; + r = allread(fd, s, SIZE_FMT - 1) ; + if (errno) + { + if (verbosity) strerr_warnwu2sys("read from ", "/proc/sys/kernel/random/poolsize") ; + } + else + { + s[r] = 0 ; + if (!size_scan(s, &r)) + { + if (verbosity) strerr_warnwu2sys("understand ", "/proc/sys/kernel/random/poolsize") ; + } + else len = (r + 7) >> 3 ; + } + fd_close(fd) ; + } + if (len < 32) len = 32 ; + if (len > 512) len = 512 ; + + if (verbosity >= 2) + { + s[size_fmt(s, len << 3)] = 0 ; + strerr_warni3x("reading ", s, " bits of random to make the seed") ; + } + if (flags.block) random_buf(seed, len) ; + else wcred = read_seed_nb(seed, len) ; + if (!wcred && verbosity) strerr_warnwu1x("make the seed creditable") ; + blake2s_update(&ctx, (char *)&len, sizeof(len)) ; + blake2s_update(&ctx, seed, len) ; + blake2s_final(&ctx, seed + len - 32) ; + if (verbosity >= 2) strerr_warni2x("writing seed to ", file) ; + umask(0077) ; + if (!openwritenclose_unsafe_sync(file, seed, len)) + strerr_diefu2sys(111, "write to ", file) ; + if (flags.wcred && wcred) + { + if (chmod(file, 0400) == -1 && verbosity) + strerr_warnwu3sys("mark ", file, "as creditable") ; + } + } + } + + return 0 ; +} |