/* 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 ; }