From ad5973028c42d947440cdae5e4f106152c3dda28 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Wed, 8 Jun 2022 17:53:50 +0000 Subject: Prepare for 2.6.0.0; delete s6-fillurandompool; add rngseed Signed-off-by: Laurent Bercot --- .gitignore | 2 +- AUTHORS | 1 + NEWS | 5 +- configure | 4 + doc/index.html | 4 +- doc/rngseed.html | 134 +++++++++++++++ doc/s6-fillurandompool.html | 74 -------- doc/upgrade.html | 4 +- package/deps.mak | 6 +- package/info | 2 +- package/modes | 2 +- package/targets.mak | 2 +- src/minutils/deps-exe/rngseed | 2 + src/minutils/deps-exe/s6-fillurandompool | 1 - src/minutils/rngseed.c | 279 +++++++++++++++++++++++++++++++ src/minutils/s6-fillurandompool.c | 10 -- 16 files changed, 436 insertions(+), 96 deletions(-) create mode 100644 doc/rngseed.html delete mode 100644 doc/s6-fillurandompool.html create mode 100644 src/minutils/deps-exe/rngseed delete mode 100644 src/minutils/deps-exe/s6-fillurandompool create mode 100644 src/minutils/rngseed.c delete mode 100644 src/minutils/s6-fillurandompool.c diff --git a/.gitignore b/.gitignore index bb472fe..43ea92b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ *.so.* /config.mak /src/include/s6-linux-utils/config.h +/rngseed /s6-chroot -/s6-fillurandompool /s6-freeramdisk /s6-hostname /s6-logwatch diff --git a/AUTHORS b/AUTHORS index 3d6eaf7..11f9c99 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,3 +11,4 @@ Thanks to: Jorge Almeida Olivier Brunel Natanael Copa + Jason Donenfeld diff --git a/NEWS b/NEWS index d418b3f..1885d6c 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,12 @@ Changelog for s6-linux-utils. -In 2.5.1.8 +In 2.6.0.0 ---------- - Adaptation to skalibs-2.12.0.0. + - s6-fillurandompool removed. + - New program: rngseed, replacing s6-fillurandompool, with +a lot more features. In 2.5.1.7 diff --git a/configure b/configure index d3fb5a0..f407635 100755 --- a/configure +++ b/configure @@ -46,6 +46,7 @@ Optional features: --enable-absolute-paths do not rely on PATH to access this package's binaries, hardcode absolute BINDIR/foobar paths instead [disabled] --enable-nsss use the nsss library for user information [disabled] + --with-seed-dir=DIR make DIR the default rngseed directory [/var/lib/rngseed] EOF exit 0 @@ -160,6 +161,7 @@ addlibdpath='' vpaths='' vpathd='' build= +seeddir=/var/lib/rngseed for arg ; do case "$arg" in @@ -192,6 +194,7 @@ for arg ; do --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;; --enable-nsss|--enable-nsss=yes) usensss=true ;; --disable-nsss|--enable-nsss=no) usensss=false ;; + --with-seed-file=*) seed=${arg#*=} ;; --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;; --host=*|--target=*) target=${arg#*=} ;; --build=*) build=${arg#*=} ;; @@ -476,6 +479,7 @@ else echo "#define ${package_macro_name}_EXTBINPREFIX \"\"" fi echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\"" +echo "#define RNGSEED_DIR \"$seeddir\"" echo echo "#endif" exec 1>&3 3>&- diff --git a/doc/index.html b/doc/index.html index 733a1c7..d8077c2 100644 --- a/doc/index.html +++ b/doc/index.html @@ -63,7 +63,7 @@ library.
  • The current released version of s6-linux-utils is -2.5.1.8.
  • +2.6.0.0.
  • Alternatively, you can checkout a copy of the s6-linux-utils git repository: @@ -100,8 +100,8 @@ the previous versions of s6-linux-utils and the current one.
    • +
    • The rngseed program
    • The s6-chroot program
    • -
    • The s6-fillurandompool program
    • The s6-freeramdisk program
    • The s6-hostname program
    • The s6-logwatch program
    • diff --git a/doc/rngseed.html b/doc/rngseed.html new file mode 100644 index 0000000..d08f56c --- /dev/null +++ b/doc/rngseed.html @@ -0,0 +1,134 @@ + + + + + + s6-linux-utils: the rngseed program + + + + + + +

      +s6-linux-utils
      +Software
      +skarnet.org +

      + +

      The rngseed program

      + +

      + rngseed manipulates the Linux kernel's entropy pool. It can seed +the kernel's random number generator from a file, save a new seed into a +file, wait until the entropy pool is full, and so on. Please read the +options list carefully in order to use it in a secure way. +

      + +

      + rngseed can only be run as root. +

      + +

      Interface

      + +
      +     rngseed [ -r | -R ] [ -N | -n ] [ -w | -W ] [ -d dir ] [ -v verbosity ]
      +
      + +

      + The behaviour of rngseed depends on what options it is given. By default, it +just waits until the entropy pool is full, then exits 0. +

      + +

      Options

      + +

      Configuration options

      + +
        +
      • -v verbosity : be more or less verbose. +Default is 1, meaning rngseed will print warning and error +messages. 0 will make it only print error messages, not warnings. +2 or more will make it add informational messages.
      • +
      • -d dir : use dir as the +directory where the seed file is located (for reading as well as writing). +dir must be located on a writable, permanent filesystem. +Default is /var/lib/rngseed.
      • +
      + +

      Behaviour options

      + +
        +
      • -r : read from a seed file. rngseed will attempt to read +some bits from dir/seed and seed the kernel's RNG with the data. +dir must be on a writable filesystem, because the seed file will be unlinked +(the same data must not be used to seed the RNG twice). rngseed -r is +typically used at boot time, in init scripts, right after mounting the +filesystem where the seed has been saved.
      • +
      • -R : read from a seed file, ignoring creditability. +Behaves like -r, but will not increase the entropy count of the +kernel RNG even if the seed file is marked as creditable.
      • +
      • -w : write to a seed file. rngseed will save some +random bits into dir/seed, marking the seed as creditable if the +RNG's entropy pool is fully initialized. rngseed -w is typically used at +shutdown time, right before unmounting filesystems; the point is to store +a seed on disk so it can be reused on next boot by rngseed -r.
      • +
      • -W : write to a seed file, without registering +creditability. Behaves like -w, but does not mark the new seed +file as creditable.
      • +
      • -N : block. After reading a seed file if required, +and before writing a new seed file if required, rngseed will wait until the +entropy pool is ready. This ensures that future readings of the kernel +RNG will be cryptographically secure, and that new seed files will be +creditable. This is the default.
      • +
      • -n : do not block. Immediately proceed even if the entropy +pool is not ready. This may make a new seed file non-creditable.
      • +
      + +

      Creditability

      + +

      + A seed is said to be creditable if it has been obtained through a +cryptographically secure RNG. This means it is safe from replay attacks, and +safe to use to count towards the entropy pool when seeding the kernel RNG. +rngseed -w will normally always create a creditable seed file, +especially if used at shutdown time: by then, the kernel's entropy pool +should have been initialized for a while. +

      + +

      + An uncreditable seed can be used to add to the random pool, but +should not increment the entropy count, because it is not safe from +replay attacks. rngseed -r will do the right thing if the seed +it reads is uncreditable. +

      + +

      + rngseed uses the seed file's permissions to mark creditability. +An uncreditable seed has rights 0600; a creditable seed has rights 0400. +

      + +

      Exit codes

      + +
        +
      • 0: success
      • +
      • 100: wrong usage
      • +
      • 111: system call failure
      • +
      + +

      Notes

      + +
        +
      • rngseed -N replaces the old s6-fillurandompool program, +that only waited for the entropy pool to get ready, but did not include any +seed file management.
      • +
      • The options are named r and w from the seed file's +point of view. +rngseed -r reads from the file (and unlinks it) and writes to the kernel +RNG. rngseed -w reads from the kernel RNG and writes to the file.
      • +
      • rngseed is inspired by Jason Donenfeld's +seedrng program. It is, +however, an independent implementation of the same concept.
      • +
      + + + diff --git a/doc/s6-fillurandompool.html b/doc/s6-fillurandompool.html deleted file mode 100644 index c07b0f4..0000000 --- a/doc/s6-fillurandompool.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - s6-linux-utils: the s6-fillurandompool program - - - - - - -

      -s6-linux-utils
      -Software
      -skarnet.org -

      - -

      The s6-fillurandompool program

      - -

      -s6-fillurandompool blocks until the machine's -/dev/urandom entropy pool is filled up. Then it exits. -

      - -

      Interface

      - -
      -     s6-fillurandompool
      -
      - -

      Rationale

      - -

      - For some reason, Linux has two separate entropy pools: one for -/dev/random and one for /dev/urandom. -

      - -

      - Reading from /dev/random blocks when its entropy pool is -not full enough, so it will never return weak random data. (Reading -from /dev/random is overkill anyway, and -you -should not be doing it.) -

      - -

      - However, reading from /dev/urandom (which -you should be doing) -will not block, even though the entropy pool may not have been -initialized yet. That's the only insecure thing about it: at boot time, -/dev/urandom may return weak random data, until its entropy -pool has filled up. -

      - -

      - s6-fillurandompool is meant to address this issue. Call it once -early on in your boot scripts, before you need any serious random data; -when it exits, the /dev/urandom pool has been properly initialized, -and it is now safe to read from /dev/urandom every time you need -random data, until the machine shuts down. -

      - -

      Notes

      - -
        -
      • s6-fillurandompool will only work on a Linux kernel version -3.17 or later: this is when the -getrandom() -system call, which it internally uses, has been implemented.
      • -
      - - - diff --git a/doc/upgrade.html b/doc/upgrade.html index e8e658e..bc39329 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -18,11 +18,13 @@

      What has changed in s6-linux-utils

      -

      in 2.5.1.8

      +

      in 2.6.0.0

      • skalibs dependency bumped to 2.12.0.0.
      • +
      • s6-fillurandompool has been replaced with the new +rngseed program.

      in 2.5.1.7

      diff --git a/package/deps.mak b/package/deps.mak index c464c91..3150147 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -2,8 +2,8 @@ # This file has been generated by tools/gen-deps.sh # +src/minutils/rngseed.o src/minutils/rngseed.lo: src/minutils/rngseed.c src/include/s6-linux-utils/config.h src/minutils/s6-chroot.o src/minutils/s6-chroot.lo: src/minutils/s6-chroot.c -src/minutils/s6-fillurandompool.o src/minutils/s6-fillurandompool.lo: src/minutils/s6-fillurandompool.c src/minutils/s6-freeramdisk.o src/minutils/s6-freeramdisk.lo: src/minutils/s6-freeramdisk.c src/minutils/s6-hostname.o src/minutils/s6-hostname.lo: src/minutils/s6-hostname.c src/minutils/s6-logwatch.o src/minutils/s6-logwatch.lo: src/minutils/s6-logwatch.c @@ -21,10 +21,10 @@ src/minutils/s6ps_statparse.o src/minutils/s6ps_statparse.lo: src/minutils/s6ps_ src/minutils/s6ps_ttycache.o src/minutils/s6ps_ttycache.lo: src/minutils/s6ps_ttycache.c src/minutils/s6-ps.h src/minutils/s6ps_wchan.o src/minutils/s6ps_wchan.lo: src/minutils/s6ps_wchan.c src/minutils/s6-ps.h +rngseed: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} +rngseed: src/minutils/rngseed.o s6-chroot: EXTRA_LIBS := -lskarnet s6-chroot: src/minutils/s6-chroot.o -s6-fillurandompool: EXTRA_LIBS := -lskarnet -s6-fillurandompool: src/minutils/s6-fillurandompool.o s6-freeramdisk: EXTRA_LIBS := -lskarnet s6-freeramdisk: src/minutils/s6-freeramdisk.o s6-hostname: EXTRA_LIBS := -lskarnet diff --git a/package/info b/package/info index bcfa0a7..b6f6275 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=s6-linux-utils -version=2.5.1.8 +version=2.6.0.0 category=admin package_macro_name=S6_LINUX_UTILS diff --git a/package/modes b/package/modes index 5c61589..46b4623 100644 --- a/package/modes +++ b/package/modes @@ -1,5 +1,5 @@ +rngseed 0744 s6-chroot 0744 -s6-fillurandompool 0755 s6-freeramdisk 0744 s6-hostname 0755 s6-logwatch 0755 diff --git a/package/targets.mak b/package/targets.mak index 0fa35ef..f42cb5a 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -1,6 +1,6 @@ BIN_TARGETS := \ +rngseed \ s6-chroot \ -s6-fillurandompool \ s6-freeramdisk \ s6-hostname \ s6-logwatch \ diff --git a/src/minutils/deps-exe/rngseed b/src/minutils/deps-exe/rngseed new file mode 100644 index 0000000..a11a5f4 --- /dev/null +++ b/src/minutils/deps-exe/rngseed @@ -0,0 +1,2 @@ +-lskarnet +${SYSCLOCK_LIB} diff --git a/src/minutils/deps-exe/s6-fillurandompool b/src/minutils/deps-exe/s6-fillurandompool deleted file mode 100644 index e7187fe..0000000 --- a/src/minutils/deps-exe/s6-fillurandompool +++ /dev/null @@ -1 +0,0 @@ --lskarnet 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 + +#ifndef SKALIBS_HASCLOCKBOOT +# error "CLOCK_BOOTTIME required" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 ; +} diff --git a/src/minutils/s6-fillurandompool.c b/src/minutils/s6-fillurandompool.c deleted file mode 100644 index c96437c..0000000 --- a/src/minutils/s6-fillurandompool.c +++ /dev/null @@ -1,10 +0,0 @@ -/* ISC license. */ - -#include - -int main (void) -{ - char c ; - random_buf(&c, 1) ; - return 0 ; -} -- cgit v1.3.1