1 1.41 riastrad /* $NetBSD: rndctl.c,v 1.41 2023/04/11 13:17:32 riastradh Exp $ */ 2 1.3 perry 3 1.1 explorer /*- 4 1.1 explorer * Copyright (c) 1997 Michael Graff. 5 1.1 explorer * All rights reserved. 6 1.1 explorer * 7 1.1 explorer * Redistribution and use in source and binary forms, with or without 8 1.1 explorer * modification, are permitted provided that the following conditions 9 1.1 explorer * are met: 10 1.1 explorer * 1. Redistributions of source code must retain the above copyright 11 1.1 explorer * notice, this list of conditions and the following disclaimer. 12 1.1 explorer * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 explorer * notice, this list of conditions and the following disclaimer in the 14 1.1 explorer * documentation and/or other materials provided with the distribution. 15 1.1 explorer * 3. Neither the name of the author nor the names of other contributors 16 1.1 explorer * may be used to endorse or promote products derived from this software 17 1.1 explorer * without specific prior written permission. 18 1.1 explorer * 19 1.1 explorer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 explorer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 explorer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 explorer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 explorer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 1.1 explorer * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 1.1 explorer * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 1.1 explorer * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 1.1 explorer * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 explorer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 explorer * SUCH DAMAGE. 30 1.1 explorer */ 31 1.33 riastrad 32 1.15 agc #include <sys/cdefs.h> 33 1.15 agc #ifndef lint 34 1.41 riastrad __RCSID("$NetBSD: rndctl.c,v 1.41 2023/04/11 13:17:32 riastradh Exp $"); 35 1.15 agc #endif 36 1.15 agc 37 1.33 riastrad #include <sys/param.h> 38 1.11 enami #include <sys/types.h> 39 1.33 riastrad #include <sys/endian.h> 40 1.11 enami #include <sys/ioctl.h> 41 1.30 riastrad #include <sys/rndio.h> 42 1.31 riastrad #include <sys/sha3.h> 43 1.36 riastrad #include <sys/sysctl.h> 44 1.11 enami 45 1.33 riastrad #include <err.h> 46 1.33 riastrad #include <errno.h> 47 1.33 riastrad #include <fcntl.h> 48 1.33 riastrad #include <paths.h> 49 1.33 riastrad #include <sha1.h> 50 1.1 explorer #include <stdio.h> 51 1.1 explorer #include <stdlib.h> 52 1.33 riastrad #include <string.h> 53 1.1 explorer #include <unistd.h> 54 1.1 explorer 55 1.1 explorer typedef struct { 56 1.17 christos const char *a_name; 57 1.9 enami u_int32_t a_type; 58 1.1 explorer } arg_t; 59 1.1 explorer 60 1.20 joerg static const arg_t source_types[] = { 61 1.6 sommerfe { "???", RND_TYPE_UNKNOWN }, 62 1.1 explorer { "disk", RND_TYPE_DISK }, 63 1.1 explorer { "net", RND_TYPE_NET }, 64 1.1 explorer { "tape", RND_TYPE_TAPE }, 65 1.1 explorer { "tty", RND_TYPE_TTY }, 66 1.11 enami { "rng", RND_TYPE_RNG }, 67 1.24 tls { "skew", RND_TYPE_SKEW }, 68 1.24 tls { "env", RND_TYPE_ENV }, 69 1.24 tls { "vm", RND_TYPE_VM }, 70 1.24 tls { "power", RND_TYPE_POWER }, 71 1.1 explorer { NULL, 0 } 72 1.1 explorer }; 73 1.1 explorer 74 1.20 joerg __dead static void usage(void); 75 1.20 joerg static u_int32_t find_type(const char *name); 76 1.20 joerg static const char *find_name(u_int32_t); 77 1.20 joerg static void do_ioctl(rndctl_t *); 78 1.38 nia static char * strflags(uint32_t, u_int32_t); 79 1.20 joerg static void do_list(int, u_int32_t, char *); 80 1.40 nia static void do_print_source(rndsource_est_t *); 81 1.40 nia static void do_print_source_verbose(rndsource_est_t *); 82 1.20 joerg static void do_stats(void); 83 1.2 explorer 84 1.34 riastrad static int iflag; 85 1.28 tls static int vflag; 86 1.28 tls 87 1.2 explorer static void 88 1.1 explorer usage(void) 89 1.1 explorer { 90 1.9 enami 91 1.29 wiz fprintf(stderr, "usage: %s [-CEce] [-d devname | -t devtype]\n", 92 1.11 enami getprogname()); 93 1.29 wiz fprintf(stderr, " %s [-lsv] [-d devname | -t devtype]\n", 94 1.11 enami getprogname()); 95 1.37 simonb fprintf(stderr, " %s [-i] -L save-file\n", getprogname()); 96 1.37 simonb fprintf(stderr, " %s -S save-file\n", getprogname()); 97 1.5 mycroft exit(1); 98 1.1 explorer } 99 1.1 explorer 100 1.20 joerg static u_int32_t 101 1.20 joerg find_type(const char *name) 102 1.1 explorer { 103 1.20 joerg const arg_t *a; 104 1.1 explorer 105 1.1 explorer a = source_types; 106 1.9 enami 107 1.9 enami while (a->a_name != NULL) { 108 1.9 enami if (strcmp(a->a_name, name) == 0) 109 1.9 enami return (a->a_type); 110 1.1 explorer a++; 111 1.1 explorer } 112 1.1 explorer 113 1.10 enami errx(1, "device name %s unknown", name); 114 1.9 enami return (0); 115 1.1 explorer } 116 1.1 explorer 117 1.20 joerg static const char * 118 1.1 explorer find_name(u_int32_t type) 119 1.1 explorer { 120 1.20 joerg const arg_t *a; 121 1.1 explorer 122 1.1 explorer a = source_types; 123 1.9 enami 124 1.9 enami while (a->a_name != NULL) { 125 1.9 enami if (type == a->a_type) 126 1.9 enami return (a->a_name); 127 1.1 explorer a++; 128 1.1 explorer } 129 1.1 explorer 130 1.10 enami warnx("device type %u unknown", type); 131 1.10 enami return ("???"); 132 1.1 explorer } 133 1.1 explorer 134 1.34 riastrad static int 135 1.35 riastrad update_seed(const char *filename, int fd_seed, const char *tmp, 136 1.35 riastrad const void *extra, size_t nextra, uint32_t extraentropy) 137 1.21 tls { 138 1.31 riastrad uint32_t systementropy; 139 1.31 riastrad uint8_t buf[32]; 140 1.31 riastrad SHAKE128_CTX shake128; 141 1.21 tls rndsave_t rs; 142 1.21 tls SHA1_CTX s; 143 1.31 riastrad ssize_t nread, nwrit; 144 1.35 riastrad int fd_random; 145 1.21 tls 146 1.31 riastrad /* Paranoia: Avoid stack memory disclosure. */ 147 1.31 riastrad memset(&rs, 0, sizeof rs); 148 1.25 jruoho 149 1.35 riastrad /* Open /dev/urandom to read data from the system. */ 150 1.35 riastrad if ((fd_random = open(_PATH_URANDOM, O_RDONLY)) == -1) { 151 1.35 riastrad warn("open /dev/urandom"); 152 1.34 riastrad return -1; 153 1.34 riastrad } 154 1.21 tls 155 1.31 riastrad /* Find how much entropy is in the pool. */ 156 1.35 riastrad if (ioctl(fd_random, RNDGETENTCNT, &systementropy) == -1) { 157 1.34 riastrad warn("ioctl(RNDGETENTCNT)"); 158 1.34 riastrad systementropy = 0; 159 1.34 riastrad } 160 1.31 riastrad 161 1.31 riastrad /* Read some data from /dev/urandom. */ 162 1.35 riastrad if ((size_t)(nread = read(fd_random, buf, sizeof buf)) != sizeof buf) { 163 1.31 riastrad if (nread == -1) 164 1.34 riastrad warn("read"); 165 1.31 riastrad else 166 1.34 riastrad warnx("truncated read"); 167 1.34 riastrad return -1; 168 1.31 riastrad } 169 1.31 riastrad 170 1.31 riastrad /* Close /dev/urandom; we're done with it. */ 171 1.35 riastrad if (close(fd_random) == -1) 172 1.31 riastrad warn("close"); 173 1.35 riastrad fd_random = -1; /* paranoia */ 174 1.21 tls 175 1.31 riastrad /* 176 1.31 riastrad * Hash what we read together with the extra input to generate 177 1.31 riastrad * the seed data. 178 1.31 riastrad */ 179 1.31 riastrad SHAKE128_Init(&shake128); 180 1.31 riastrad SHAKE128_Update(&shake128, buf, sizeof buf); 181 1.31 riastrad SHAKE128_Update(&shake128, extra, nextra); 182 1.31 riastrad SHAKE128_Final(rs.data, sizeof(rs.data), &shake128); 183 1.31 riastrad explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */ 184 1.21 tls 185 1.31 riastrad /* 186 1.31 riastrad * Report an upper bound on the min-entropy of the seed data. 187 1.31 riastrad * We take the larger of the system entropy and the extra 188 1.31 riastrad * entropy -- the system state and the extra input may or may 189 1.31 riastrad * not be independent, so we can't add them -- and clamp to the 190 1.31 riastrad * size of the data. 191 1.31 riastrad */ 192 1.31 riastrad systementropy = MIN(systementropy, 193 1.31 riastrad MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY); 194 1.31 riastrad extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY); 195 1.31 riastrad rs.entropy = MIN(MAX(systementropy, extraentropy), 196 1.31 riastrad MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY); 197 1.21 tls 198 1.31 riastrad /* 199 1.32 riastrad * Compute the checksum on the 32-bit entropy count, followed 200 1.32 riastrad * by the seed data. 201 1.31 riastrad */ 202 1.21 tls SHA1Init(&s); 203 1.31 riastrad SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy)); 204 1.21 tls SHA1Update(&s, rs.data, sizeof(rs.data)); 205 1.21 tls SHA1Final(rs.digest, &s); 206 1.31 riastrad explicit_memset(&s, 0, sizeof s); /* paranoia */ 207 1.21 tls 208 1.31 riastrad /* 209 1.31 riastrad * Write it to a temporary file and sync it before we commit. 210 1.31 riastrad * This way either the old seed or the new seed is completely 211 1.31 riastrad * written in the expected location on disk even if the system 212 1.31 riastrad * crashes as long as the file system doesn't get corrupted too 213 1.31 riastrad * badly. 214 1.31 riastrad * 215 1.31 riastrad * If interrupted after this point and the temporary file is 216 1.31 riastrad * disclosed, no big deal -- either the pool was predictable to 217 1.31 riastrad * begin with in which case we're hosed either way, or we've 218 1.31 riastrad * just revealed some output which is not a problem. 219 1.31 riastrad */ 220 1.35 riastrad if ((size_t)(nwrit = write(fd_seed, &rs, sizeof rs)) != sizeof rs) { 221 1.31 riastrad int error = errno; 222 1.31 riastrad if (unlink(tmp) == -1) 223 1.31 riastrad warn("unlink"); 224 1.31 riastrad if (nwrit == -1) 225 1.34 riastrad warnc(error, "write"); 226 1.31 riastrad else 227 1.34 riastrad warnx("truncated write"); 228 1.34 riastrad return -1; 229 1.31 riastrad } 230 1.31 riastrad explicit_memset(&rs, 0, sizeof rs); /* paranoia */ 231 1.35 riastrad if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1) { 232 1.31 riastrad int error = errno; 233 1.31 riastrad if (unlink(tmp) == -1) 234 1.31 riastrad warn("unlink"); 235 1.34 riastrad warnc(error, "fsync_range"); 236 1.34 riastrad return -1; 237 1.31 riastrad } 238 1.35 riastrad if (close(fd_seed) == -1) 239 1.31 riastrad warn("close"); 240 1.31 riastrad 241 1.31 riastrad /* Rename it over the original file to commit. */ 242 1.34 riastrad if (rename(tmp, filename) == -1) { 243 1.34 riastrad warn("rename"); 244 1.34 riastrad return -1; 245 1.34 riastrad } 246 1.34 riastrad 247 1.34 riastrad /* Success! */ 248 1.34 riastrad return 0; 249 1.34 riastrad } 250 1.34 riastrad 251 1.34 riastrad static void 252 1.34 riastrad do_save(const char *filename) 253 1.34 riastrad { 254 1.35 riastrad char tmp[PATH_MAX]; 255 1.35 riastrad int fd_seed; 256 1.35 riastrad 257 1.36 riastrad /* Consolidate any pending samples. */ 258 1.36 riastrad if (sysctlbyname("kern.entropy.consolidate", NULL, NULL, 259 1.36 riastrad (const int[]){1}, sizeof(int)) == -1) 260 1.36 riastrad warn("consolidate entropy"); 261 1.36 riastrad 262 1.35 riastrad /* Format the temporary file name. */ 263 1.35 riastrad if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX) 264 1.35 riastrad errx(1, "path too long"); 265 1.34 riastrad 266 1.35 riastrad /* Create a temporary seed file. */ 267 1.35 riastrad if ((fd_seed = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1) 268 1.35 riastrad err(1, "open seed file to save"); 269 1.35 riastrad 270 1.35 riastrad /* Update the seed. Abort on failure. */ 271 1.35 riastrad if (update_seed(filename, fd_seed, tmp, NULL, 0, 0) == -1) 272 1.34 riastrad exit(1); 273 1.21 tls } 274 1.21 tls 275 1.21 tls static void 276 1.31 riastrad do_load(const char *filename) 277 1.21 tls { 278 1.31 riastrad char tmp[PATH_MAX]; 279 1.35 riastrad int fd_new, fd_old, fd_random; 280 1.31 riastrad rndsave_t rs; 281 1.21 tls rnddata_t rd; 282 1.31 riastrad ssize_t nread, nwrit; 283 1.21 tls SHA1_CTX s; 284 1.21 tls uint8_t digest[SHA1_DIGEST_LENGTH]; 285 1.34 riastrad int ro = 0, fail = 0; 286 1.34 riastrad int error; 287 1.21 tls 288 1.31 riastrad /* 289 1.31 riastrad * 1. Load the old seed. 290 1.35 riastrad * 2. Feed the old seed into the kernel. 291 1.35 riastrad * 3. Generate and write a new seed. 292 1.34 riastrad * 4. Erase the old seed if we can. 293 1.31 riastrad * 294 1.35 riastrad * We follow the procedure in 295 1.31 riastrad * 296 1.31 riastrad * Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno, 297 1.31 riastrad * _Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2 298 1.35 riastrad * `Update Seed File'. 299 1.31 riastrad * 300 1.35 riastrad * Additionally, we zero the seed's stored entropy estimate if 301 1.35 riastrad * it appears to be on a read-only medium. 302 1.31 riastrad */ 303 1.21 tls 304 1.31 riastrad /* Format the temporary file name. */ 305 1.31 riastrad if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX) 306 1.31 riastrad errx(1, "path too long"); 307 1.31 riastrad 308 1.35 riastrad /* Create a new seed file or determine the medium is read-only. */ 309 1.35 riastrad if ((fd_new = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1) { 310 1.35 riastrad warn("update seed file"); 311 1.35 riastrad ro = 1; 312 1.35 riastrad } 313 1.35 riastrad 314 1.34 riastrad /* 315 1.34 riastrad * 1. Load the old seed. 316 1.34 riastrad */ 317 1.35 riastrad if ((fd_old = open(filename, O_RDWR)) == -1) { 318 1.34 riastrad error = errno; 319 1.34 riastrad if ((error != EPERM && error != EROFS) || 320 1.35 riastrad (fd_old = open(filename, O_RDONLY)) == -1) 321 1.34 riastrad err(1, "open seed file to load"); 322 1.35 riastrad if (fd_new != -1) 323 1.35 riastrad warnc(error, "can't overwrite old seed file"); 324 1.34 riastrad ro = 1; 325 1.34 riastrad } 326 1.35 riastrad if ((size_t)(nread = read(fd_old, &rs, sizeof rs)) != sizeof rs) { 327 1.31 riastrad if (nread == -1) 328 1.31 riastrad err(1, "read seed"); 329 1.31 riastrad else 330 1.31 riastrad errx(1, "seed too short"); 331 1.21 tls } 332 1.21 tls 333 1.31 riastrad /* Verify its checksum. */ 334 1.21 tls SHA1Init(&s); 335 1.31 riastrad SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy)); 336 1.21 tls SHA1Update(&s, rs.data, sizeof(rs.data)); 337 1.21 tls SHA1Final(digest, &s); 338 1.31 riastrad if (!consttime_memequal(digest, rs.digest, sizeof(digest))) { 339 1.31 riastrad /* 340 1.31 riastrad * If the checksum doesn't match, doesn't hurt to feed 341 1.31 riastrad * the seed in anyway, but act as though it has zero 342 1.31 riastrad * entropy in case it was corrupted with predictable 343 1.31 riastrad * garbage. 344 1.31 riastrad */ 345 1.31 riastrad warnx("bad checksum"); 346 1.31 riastrad rs.entropy = 0; 347 1.21 tls } 348 1.21 tls 349 1.32 riastrad /* 350 1.32 riastrad * If the entropy is insensibly large, try byte-swapping. 351 1.32 riastrad * Otherwise assume the file is corrupted and act as though it 352 1.32 riastrad * has zero entropy. 353 1.32 riastrad */ 354 1.32 riastrad if (howmany(rs.entropy, NBBY) > sizeof(rs.data)) { 355 1.32 riastrad rs.entropy = bswap32(rs.entropy); 356 1.35 riastrad if (howmany(rs.entropy, NBBY) > sizeof(rs.data)) { 357 1.35 riastrad warnx("bad entropy estimate"); 358 1.32 riastrad rs.entropy = 0; 359 1.35 riastrad } 360 1.32 riastrad } 361 1.32 riastrad 362 1.35 riastrad /* If the medium can't be updated, zero the entropy estimate. */ 363 1.35 riastrad if (ro) 364 1.34 riastrad rs.entropy = 0; 365 1.34 riastrad 366 1.35 riastrad /* Fail later on if there's no entropy in the seed. */ 367 1.35 riastrad if (rs.entropy == 0) { 368 1.35 riastrad warnx("no entropy in seed"); 369 1.35 riastrad fail = 1; 370 1.35 riastrad } 371 1.35 riastrad 372 1.35 riastrad /* If the user asked, zero the entropy estimate, but succeed. */ 373 1.35 riastrad if (iflag) 374 1.34 riastrad rs.entropy = 0; 375 1.34 riastrad 376 1.34 riastrad /* 377 1.35 riastrad * 2. Feed the old seed into the kernel. 378 1.36 riastrad * 379 1.36 riastrad * This also has the effect of consolidating pending samples, 380 1.36 riastrad * whether or not there are enough samples from sources deemed 381 1.36 riastrad * to have full entropy, so that the updated seed will 382 1.36 riastrad * incorporate them. 383 1.34 riastrad */ 384 1.21 tls rd.len = MIN(sizeof(rd.data), sizeof(rs.data)); 385 1.21 tls rd.entropy = rs.entropy; 386 1.31 riastrad memcpy(rd.data, rs.data, rd.len); 387 1.31 riastrad explicit_memset(&rs, 0, sizeof rs); /* paranoia */ 388 1.31 riastrad if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1) 389 1.31 riastrad err(1, "open /dev/urandom"); 390 1.31 riastrad if (ioctl(fd_random, RNDADDDATA, &rd) == -1) 391 1.31 riastrad err(1, "RNDADDDATA"); 392 1.34 riastrad explicit_memset(&rd, 0, sizeof rd); /* paranoia */ 393 1.31 riastrad if (close(fd_random) == -1) 394 1.31 riastrad warn("close /dev/urandom"); 395 1.31 riastrad fd_random = -1; /* paranoia */ 396 1.21 tls 397 1.31 riastrad /* 398 1.35 riastrad * 3. Generate and write a new seed. 399 1.35 riastrad */ 400 1.35 riastrad if (fd_new == -1 || 401 1.35 riastrad update_seed(filename, fd_new, tmp, rs.data, sizeof(rs.data), 402 1.35 riastrad rs.entropy) == -1) 403 1.35 riastrad fail = 1; 404 1.35 riastrad 405 1.35 riastrad /* 406 1.35 riastrad * 4. Erase the old seed. 407 1.35 riastrad * 408 1.35 riastrad * Only effective if we're on a fixed-address file system like 409 1.35 riastrad * ffs -- doesn't help to erase the data on lfs, but doesn't 410 1.35 riastrad * hurt either. No need to unlink because update_seed will 411 1.35 riastrad * have already renamed over it. 412 1.31 riastrad */ 413 1.34 riastrad if (!ro) { 414 1.34 riastrad memset(&rs, 0, sizeof rs); 415 1.35 riastrad if ((size_t)(nwrit = pwrite(fd_old, &rs, sizeof rs, 0)) != 416 1.34 riastrad sizeof rs) { 417 1.34 riastrad if (nwrit == -1) 418 1.34 riastrad err(1, "overwrite old seed"); 419 1.34 riastrad else 420 1.34 riastrad errx(1, "truncated overwrite"); 421 1.34 riastrad } 422 1.35 riastrad if (fsync_range(fd_old, FDATASYNC|FDISKSYNC, 0, 0) == -1) 423 1.34 riastrad err(1, "fsync_range"); 424 1.21 tls } 425 1.34 riastrad 426 1.35 riastrad /* Fail noisily if anything went wrong. */ 427 1.34 riastrad if (fail) 428 1.35 riastrad exit(1); 429 1.21 tls } 430 1.21 tls 431 1.21 tls static void 432 1.1 explorer do_ioctl(rndctl_t *rctl) 433 1.1 explorer { 434 1.1 explorer int fd; 435 1.1 explorer int res; 436 1.1 explorer 437 1.25 jruoho fd = open(_PATH_URANDOM, O_RDONLY, 0644); 438 1.1 explorer if (fd < 0) 439 1.1 explorer err(1, "open"); 440 1.1 explorer 441 1.1 explorer res = ioctl(fd, RNDCTL, rctl); 442 1.1 explorer if (res < 0) 443 1.1 explorer err(1, "ioctl(RNDCTL)"); 444 1.1 explorer 445 1.1 explorer close(fd); 446 1.1 explorer } 447 1.1 explorer 448 1.20 joerg static char * 449 1.38 nia strflags(uint32_t totalbits, u_int32_t fl) 450 1.1 explorer { 451 1.1 explorer static char str[512]; 452 1.1 explorer 453 1.28 tls str[0] = '\0'; 454 1.38 nia if (totalbits > 0 && (fl & RND_FLAG_NO_ESTIMATE) == 0) 455 1.28 tls strlcat(str, "estimate, ", sizeof(str)); 456 1.9 enami 457 1.38 nia if ((fl & RND_FLAG_NO_COLLECT) == 0) 458 1.28 tls strlcat(str, "collect, ", sizeof(str)); 459 1.28 tls 460 1.28 tls if (fl & RND_FLAG_COLLECT_VALUE) 461 1.28 tls strlcat(str, "v, ", sizeof(str)); 462 1.28 tls if (fl & RND_FLAG_COLLECT_TIME) 463 1.28 tls strlcat(str, "t, ", sizeof(str)); 464 1.28 tls 465 1.28 tls if (str[strlen(str) - 2] == ',') 466 1.28 tls str[strlen(str) - 2] = '\0'; 467 1.9 enami 468 1.9 enami return (str); 469 1.1 explorer } 470 1.1 explorer 471 1.40 nia #define HEADER "Source Estimated bits Samples Type Flags\n" 472 1.40 nia 473 1.40 nia static void 474 1.40 nia do_print_source(rndsource_est_t *source) 475 1.40 nia { 476 1.40 nia printf("%-16s ", source->rt.name); 477 1.40 nia printf("%10" PRIu32 " ", source->rt.total); 478 1.40 nia printf("%10" PRIu32 " ", source->dt_samples + source->dv_samples); 479 1.40 nia printf("%-6s ", find_name(source->rt.type)); 480 1.40 nia printf("%s\n", strflags(source->rt.total, source->rt.flags)); 481 1.40 nia } 482 1.40 nia 483 1.40 nia static void 484 1.40 nia do_print_source_verbose(rndsource_est_t *source) 485 1.40 nia { 486 1.40 nia printf("\tDt samples = %d\n", source->dt_samples); 487 1.40 nia printf("\tDt bits = %d\n", source->dt_total); 488 1.40 nia printf("\tDv samples = %d\n", source->dv_samples); 489 1.40 nia printf("\tDv bits = %d\n", source->dv_total); 490 1.40 nia } 491 1.1 explorer 492 1.20 joerg static void 493 1.1 explorer do_list(int all, u_int32_t type, char *name) 494 1.1 explorer { 495 1.28 tls rndstat_est_t rstat; 496 1.28 tls rndstat_est_name_t rstat_name; 497 1.9 enami int fd; 498 1.9 enami int res; 499 1.19 lukem uint32_t i; 500 1.9 enami u_int32_t start; 501 1.1 explorer 502 1.25 jruoho fd = open(_PATH_URANDOM, O_RDONLY, 0644); 503 1.1 explorer if (fd < 0) 504 1.1 explorer err(1, "open"); 505 1.1 explorer 506 1.40 nia if (!all && type == 0xff) { 507 1.14 itojun strncpy(rstat_name.name, name, sizeof(rstat_name.name)); 508 1.28 tls res = ioctl(fd, RNDGETESTNAME, &rstat_name); 509 1.1 explorer if (res < 0) 510 1.28 tls err(1, "ioctl(RNDGETESTNAME)"); 511 1.1 explorer printf(HEADER); 512 1.40 nia do_print_source(&rstat_name.source); 513 1.40 nia if (vflag) 514 1.40 nia do_print_source_verbose(&rstat_name.source); 515 1.1 explorer close(fd); 516 1.1 explorer return; 517 1.1 explorer } 518 1.1 explorer 519 1.1 explorer /* 520 1.9 enami * Run through all the devices present in the system, and either 521 1.1 explorer * print out ones that match, or print out all of them. 522 1.1 explorer */ 523 1.1 explorer printf(HEADER); 524 1.1 explorer start = 0; 525 1.1 explorer for (;;) { 526 1.1 explorer rstat.count = RND_MAXSTATCOUNT; 527 1.1 explorer rstat.start = start; 528 1.28 tls res = ioctl(fd, RNDGETESTNUM, &rstat); 529 1.1 explorer if (res < 0) 530 1.28 tls err(1, "ioctl(RNDGETESTNUM)"); 531 1.9 enami 532 1.1 explorer if (rstat.count == 0) 533 1.1 explorer break; 534 1.9 enami 535 1.19 lukem for (i = 0; i < rstat.count; i++) { 536 1.40 nia if (all || type == rstat.source[i].rt.type) { 537 1.40 nia do_print_source(&rstat.source[i]); 538 1.40 nia if (vflag) 539 1.40 nia do_print_source_verbose(&rstat.source[i]); 540 1.28 tls } 541 1.28 tls } 542 1.1 explorer start += rstat.count; 543 1.1 explorer } 544 1.1 explorer 545 1.1 explorer close(fd); 546 1.1 explorer } 547 1.1 explorer 548 1.20 joerg static void 549 1.20 joerg do_stats(void) 550 1.6 sommerfe { 551 1.6 sommerfe rndpoolstat_t rs; 552 1.6 sommerfe int fd; 553 1.9 enami 554 1.25 jruoho fd = open(_PATH_URANDOM, O_RDONLY, 0644); 555 1.6 sommerfe if (fd < 0) 556 1.6 sommerfe err(1, "open"); 557 1.9 enami 558 1.6 sommerfe if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0) 559 1.6 sommerfe err(1, "ioctl(RNDGETPOOLSTAT)"); 560 1.6 sommerfe 561 1.12 enami printf("\t%9u bits currently stored in pool (max %u)\n", 562 1.6 sommerfe rs.curentropy, rs.maxentropy); 563 1.6 sommerfe 564 1.6 sommerfe close(fd); 565 1.6 sommerfe } 566 1.6 sommerfe 567 1.1 explorer int 568 1.1 explorer main(int argc, char **argv) 569 1.1 explorer { 570 1.9 enami rndctl_t rctl; 571 1.9 enami int ch, cmd, lflag, mflag, sflag; 572 1.1 explorer u_int32_t type; 573 1.41 riastrad char name[16] = ""; 574 1.21 tls const char *filename = NULL; 575 1.1 explorer 576 1.31 riastrad if (SHA3_Selftest() != 0) 577 1.31 riastrad errx(1, "SHA-3 self-test failed"); 578 1.31 riastrad 579 1.1 explorer rctl.mask = 0; 580 1.1 explorer rctl.flags = 0; 581 1.1 explorer 582 1.1 explorer cmd = 0; 583 1.1 explorer lflag = 0; 584 1.1 explorer mflag = 0; 585 1.7 joda sflag = 0; 586 1.2 explorer type = 0xff; 587 1.1 explorer 588 1.34 riastrad while ((ch = getopt(argc, argv, "CES:L:celit:d:sv")) != -1) { 589 1.9 enami switch (ch) { 590 1.1 explorer case 'C': 591 1.1 explorer rctl.flags |= RND_FLAG_NO_COLLECT; 592 1.1 explorer rctl.mask |= RND_FLAG_NO_COLLECT; 593 1.1 explorer mflag++; 594 1.1 explorer break; 595 1.1 explorer case 'E': 596 1.1 explorer rctl.flags |= RND_FLAG_NO_ESTIMATE; 597 1.1 explorer rctl.mask |= RND_FLAG_NO_ESTIMATE; 598 1.1 explorer mflag++; 599 1.1 explorer break; 600 1.21 tls case 'L': 601 1.21 tls if (cmd != 0) 602 1.21 tls usage(); 603 1.21 tls cmd = 'L'; 604 1.21 tls filename = optarg; 605 1.21 tls break; 606 1.21 tls case 'S': 607 1.21 tls if (cmd != 0) 608 1.21 tls usage(); 609 1.21 tls cmd = 'S'; 610 1.21 tls filename = optarg; 611 1.21 tls break; 612 1.1 explorer case 'c': 613 1.1 explorer rctl.flags &= ~RND_FLAG_NO_COLLECT; 614 1.1 explorer rctl.mask |= RND_FLAG_NO_COLLECT; 615 1.1 explorer mflag++; 616 1.1 explorer break; 617 1.1 explorer case 'e': 618 1.1 explorer rctl.flags &= ~RND_FLAG_NO_ESTIMATE; 619 1.1 explorer rctl.mask |= RND_FLAG_NO_ESTIMATE; 620 1.1 explorer mflag++; 621 1.1 explorer break; 622 1.34 riastrad case 'i': 623 1.34 riastrad iflag = 1; 624 1.34 riastrad break; 625 1.1 explorer case 'l': 626 1.1 explorer lflag++; 627 1.1 explorer break; 628 1.1 explorer case 't': 629 1.1 explorer if (cmd != 0) 630 1.1 explorer usage(); 631 1.1 explorer cmd = 't'; 632 1.1 explorer 633 1.1 explorer type = find_type(optarg); 634 1.1 explorer break; 635 1.1 explorer case 'd': 636 1.1 explorer if (cmd != 0) 637 1.1 explorer usage(); 638 1.1 explorer cmd = 'd'; 639 1.1 explorer 640 1.1 explorer type = 0xff; 641 1.14 itojun strlcpy(name, optarg, sizeof(name)); 642 1.1 explorer break; 643 1.6 sommerfe case 's': 644 1.6 sommerfe sflag++; 645 1.6 sommerfe break; 646 1.28 tls case 'v': 647 1.28 tls vflag++; 648 1.28 tls break; 649 1.1 explorer case '?': 650 1.1 explorer default: 651 1.1 explorer usage(); 652 1.1 explorer } 653 1.18 apb } 654 1.18 apb argc -= optind; 655 1.18 apb argv += optind; 656 1.18 apb 657 1.18 apb /* 658 1.18 apb * No leftover non-option arguments. 659 1.18 apb */ 660 1.18 apb if (argc > 0) 661 1.18 apb usage(); 662 1.1 explorer 663 1.1 explorer /* 664 1.34 riastrad * -i makes sense only with -L. 665 1.34 riastrad */ 666 1.34 riastrad if (iflag && cmd != 'L') 667 1.34 riastrad usage(); 668 1.34 riastrad 669 1.34 riastrad /* 670 1.21 tls * Save. 671 1.21 tls */ 672 1.21 tls if (cmd == 'S') { 673 1.34 riastrad do_save(filename); 674 1.21 tls exit(0); 675 1.21 tls } 676 1.21 tls 677 1.21 tls /* 678 1.21 tls * Load. 679 1.21 tls */ 680 1.21 tls if (cmd == 'L') { 681 1.21 tls do_load(filename); 682 1.21 tls exit(0); 683 1.21 tls } 684 1.21 tls 685 1.21 tls /* 686 1.9 enami * Cannot list and modify at the same time. 687 1.1 explorer */ 688 1.6 sommerfe if ((lflag != 0 || sflag != 0) && mflag != 0) 689 1.1 explorer usage(); 690 1.1 explorer 691 1.1 explorer /* 692 1.9 enami * Bomb out on no-ops. 693 1.1 explorer */ 694 1.6 sommerfe if (lflag == 0 && mflag == 0 && sflag == 0) 695 1.1 explorer usage(); 696 1.1 explorer 697 1.1 explorer /* 698 1.9 enami * Modify request. 699 1.1 explorer */ 700 1.1 explorer if (mflag != 0) { 701 1.1 explorer rctl.type = type; 702 1.14 itojun strncpy(rctl.name, name, sizeof(rctl.name)); 703 1.1 explorer do_ioctl(&rctl); 704 1.1 explorer 705 1.1 explorer exit(0); 706 1.1 explorer } 707 1.1 explorer 708 1.1 explorer /* 709 1.9 enami * List sources. 710 1.1 explorer */ 711 1.1 explorer if (lflag != 0) 712 1.1 explorer do_list(cmd == 0, type, name); 713 1.1 explorer 714 1.6 sommerfe if (sflag != 0) 715 1.6 sommerfe do_stats(); 716 1.9 enami 717 1.9 enami exit(0); 718 1.1 explorer } 719