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