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