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