1 /* $NetBSD: rndctl.c,v 1.31 2019/12/06 14:43:18 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 #include <sys/cdefs.h> 32 #include <sys/types.h> 33 #include <sha1.h> 34 35 #ifndef lint 36 __RCSID("$NetBSD: rndctl.c,v 1.31 2019/12/06 14:43:18 riastradh Exp $"); 37 #endif 38 39 40 #include <sys/types.h> 41 #include <sys/ioctl.h> 42 #include <sys/param.h> 43 #include <sys/rndio.h> 44 #include <sys/sha3.h> 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 #include <fcntl.h> 50 #include <errno.h> 51 #include <err.h> 52 #include <paths.h> 53 #include <string.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 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 -[L|S] save-file\n", getprogname()); 93 exit(1); 94 } 95 96 static u_int32_t 97 find_type(const char *name) 98 { 99 const arg_t *a; 100 101 a = source_types; 102 103 while (a->a_name != NULL) { 104 if (strcmp(a->a_name, name) == 0) 105 return (a->a_type); 106 a++; 107 } 108 109 errx(1, "device name %s unknown", name); 110 return (0); 111 } 112 113 static const char * 114 find_name(u_int32_t type) 115 { 116 const arg_t *a; 117 118 a = source_types; 119 120 while (a->a_name != NULL) { 121 if (type == a->a_type) 122 return (a->a_name); 123 a++; 124 } 125 126 warnx("device type %u unknown", type); 127 return ("???"); 128 } 129 130 static void 131 do_save(const char *filename, const void *extra, size_t nextra, 132 uint32_t extraentropy) 133 { 134 char tmp[PATH_MAX]; 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; 142 143 /* Paranoia: Avoid stack memory disclosure. */ 144 memset(&rs, 0, sizeof rs); 145 146 /* Format the temporary file name. */ 147 if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX) 148 errx(1, "path too long"); 149 150 /* Open /dev/urandom. */ 151 if ((fd = open(_PATH_URANDOM, O_RDONLY)) == -1) 152 err(1, "device open"); 153 154 /* Find how much entropy is in the pool. */ 155 if (ioctl(fd, RNDGETENTCNT, &systementropy) == -1) 156 err(1, "ioctl(RNDGETENTCNT)"); 157 158 /* Read some data from /dev/urandom. */ 159 if ((size_t)(nread = read(fd, buf, sizeof buf)) != sizeof buf) { 160 if (nread == -1) 161 err(1, "read"); 162 else 163 errx(1, "truncated read"); 164 } 165 166 /* Close /dev/urandom; we're done with it. */ 167 if (close(fd) == -1) 168 warn("close"); 169 fd = -1; /* paranoia */ 170 171 /* 172 * Hash what we read together with the extra input to generate 173 * the seed data. 174 */ 175 SHAKE128_Init(&shake128); 176 SHAKE128_Update(&shake128, buf, sizeof buf); 177 SHAKE128_Update(&shake128, extra, nextra); 178 SHAKE128_Final(rs.data, sizeof(rs.data), &shake128); 179 explicit_memset(&shake128, 0, sizeof shake128); /* paranoia */ 180 181 /* 182 * Report an upper bound on the min-entropy of the seed data. 183 * We take the larger of the system entropy and the extra 184 * entropy -- the system state and the extra input may or may 185 * not be independent, so we can't add them -- and clamp to the 186 * size of the data. 187 */ 188 systementropy = MIN(systementropy, 189 MIN(sizeof(buf), UINT32_MAX/NBBY)*NBBY); 190 extraentropy = MIN(extraentropy, MIN(nextra, UINT32_MAX/NBBY)*NBBY); 191 rs.entropy = MIN(MAX(systementropy, extraentropy), 192 MIN(sizeof(rs.data), UINT32_MAX/NBBY)*NBBY); 193 194 /* 195 * Compute the checksum on the 32-bit entropy count, in host 196 * byte order (XXX this means it is not portable across 197 * different-endian platforms!), followed 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 ((fd = open(tmp, O_CREAT|O_TRUNC|O_WRONLY, 0600)) == -1) 218 err(1, "open seed file to save"); 219 if ((size_t)(nwrit = write(fd, &rs, sizeof rs)) != sizeof rs) { 220 int error = errno; 221 if (unlink(tmp) == -1) 222 warn("unlink"); 223 if (nwrit == -1) 224 errc(1, error, "write"); 225 else 226 errx(1, "truncated write"); 227 } 228 explicit_memset(&rs, 0, sizeof rs); /* paranoia */ 229 if (fsync_range(fd, FDATASYNC|FDISKSYNC, 0, 0) == -1) { 230 int error = errno; 231 if (unlink(tmp) == -1) 232 warn("unlink"); 233 errc(1, error, "fsync_range"); 234 } 235 if (close(fd) == -1) 236 warn("close"); 237 238 /* Rename it over the original file to commit. */ 239 if (rename(tmp, filename) == -1) 240 err(1, "rename"); 241 } 242 243 static void 244 do_load(const char *filename) 245 { 246 char tmp[PATH_MAX]; 247 int fd_seed, fd_random; 248 rndsave_t rs; 249 rnddata_t rd; 250 ssize_t nread, nwrit; 251 SHA1_CTX s; 252 uint8_t digest[SHA1_DIGEST_LENGTH]; 253 254 /* 255 * The order of operations is important here: 256 * 257 * 1. Load the old seed. 258 * 2. Feed the old seed into the kernel. 259 * 3. Generate and write a new seed. 260 * 4. Erase the old seed. 261 * 262 * This follows the procedure in 263 * 264 * Niels Ferguson, Bruce Schneier, and Tadayoshi Kohno, 265 * _Cryptography Engineering_, Wiley, 2010, Sec. 9.6.2 266 * `Update Seed File'. 267 * 268 * There is a race condition: If another process generates a 269 * key from /dev/urandom after step (2) but before step (3), 270 * and if the machine crashes before step (3), an adversary who 271 * can read the disk after the crash can probably guess the 272 * complete state of the entropy pool and thereby predict the 273 * key. 274 * 275 * There's not much we can do here without some kind of 276 * systemwide lock on /dev/urandom and without introducing an 277 * opportunity for a crash to wipe out the entropy altogether. 278 * To avoid this race, you should ensure that any key 279 * generation happens _after_ `rndctl -L' has completed. 280 */ 281 282 /* Format the temporary file name. */ 283 if (snprintf(tmp, sizeof tmp, "%s.tmp", filename) >= PATH_MAX) 284 errx(1, "path too long"); 285 286 /* 1. Load the old seed. */ 287 if ((fd_seed = open(filename, O_RDWR)) == -1) 288 err(1, "open seed file to load"); 289 if ((size_t)(nread = read(fd_seed, &rs, sizeof rs)) != sizeof rs) { 290 if (nread == -1) 291 err(1, "read seed"); 292 else 293 errx(1, "seed too short"); 294 } 295 296 /* Verify its checksum. */ 297 SHA1Init(&s); 298 SHA1Update(&s, (const uint8_t *)&rs.entropy, sizeof(rs.entropy)); 299 SHA1Update(&s, rs.data, sizeof(rs.data)); 300 SHA1Final(digest, &s); 301 if (!consttime_memequal(digest, rs.digest, sizeof(digest))) { 302 /* 303 * If the checksum doesn't match, doesn't hurt to feed 304 * the seed in anyway, but act as though it has zero 305 * entropy in case it was corrupted with predictable 306 * garbage. 307 */ 308 warnx("bad checksum"); 309 rs.entropy = 0; 310 } 311 312 /* Format the ioctl request. */ 313 rd.len = MIN(sizeof(rd.data), sizeof(rs.data)); 314 rd.entropy = rs.entropy; 315 memcpy(rd.data, rs.data, rd.len); 316 explicit_memset(&rs, 0, sizeof rs); /* paranoia */ 317 318 /* 2. Feed the old seed into the kernel. */ 319 if ((fd_random = open(_PATH_URANDOM, O_WRONLY)) == -1) 320 err(1, "open /dev/urandom"); 321 if (ioctl(fd_random, RNDADDDATA, &rd) == -1) 322 err(1, "RNDADDDATA"); 323 if (close(fd_random) == -1) 324 warn("close /dev/urandom"); 325 fd_random = -1; /* paranoia */ 326 327 /* 328 * 3. Generate and write a new seed. Note that we hash the old 329 * seed together with whatever /dev/urandom returns in do_save. 330 * Why? After RNDADDDATA, the input may not be distributed 331 * immediately to /dev/urandom. 332 */ 333 do_save(filename, rd.data, rd.len, rd.entropy); 334 explicit_memset(&rd, 0, sizeof rd); /* paranoia */ 335 336 /* 337 * 4. Erase the old seed. Only effective if we're on a 338 * fixed-address file system like ffs -- doesn't help to erase 339 * the data on lfs, but doesn't hurt either. No need to unlink 340 * because do_save will have already overwritten it. 341 */ 342 memset(&rs, 0, sizeof rs); 343 if ((size_t)(nwrit = pwrite(fd_seed, &rs, sizeof rs, 0)) != 344 sizeof rs) { 345 if (nwrit == -1) 346 err(1, "overwrite old seed"); 347 else 348 errx(1, "truncated overwrite"); 349 } 350 if (fsync_range(fd_seed, FDATASYNC|FDISKSYNC, 0, 0) == -1) 351 err(1, "fsync_range"); 352 } 353 354 static void 355 do_ioctl(rndctl_t *rctl) 356 { 357 int fd; 358 int res; 359 360 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 361 if (fd < 0) 362 err(1, "open"); 363 364 res = ioctl(fd, RNDCTL, rctl); 365 if (res < 0) 366 err(1, "ioctl(RNDCTL)"); 367 368 close(fd); 369 } 370 371 static char * 372 strflags(u_int32_t fl) 373 { 374 static char str[512]; 375 376 str[0] = '\0'; 377 if (fl & RND_FLAG_NO_ESTIMATE) 378 ; 379 else 380 strlcat(str, "estimate, ", sizeof(str)); 381 382 if (fl & RND_FLAG_NO_COLLECT) 383 ; 384 else 385 strlcat(str, "collect, ", sizeof(str)); 386 387 if (fl & RND_FLAG_COLLECT_VALUE) 388 strlcat(str, "v, ", sizeof(str)); 389 if (fl & RND_FLAG_COLLECT_TIME) 390 strlcat(str, "t, ", sizeof(str)); 391 if (fl & RND_FLAG_ESTIMATE_VALUE) 392 strlcat(str, "dv, ", sizeof(str)); 393 if (fl & RND_FLAG_ESTIMATE_TIME) 394 strlcat(str, "dt, ", sizeof(str)); 395 396 if (str[strlen(str) - 2] == ',') 397 str[strlen(str) - 2] = '\0'; 398 399 return (str); 400 } 401 402 #define HEADER "Source Bits Type Flags\n" 403 404 static void 405 do_list(int all, u_int32_t type, char *name) 406 { 407 rndstat_est_t rstat; 408 rndstat_est_name_t rstat_name; 409 int fd; 410 int res; 411 uint32_t i; 412 u_int32_t start; 413 414 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 415 if (fd < 0) 416 err(1, "open"); 417 418 if (all == 0 && type == 0xff) { 419 strncpy(rstat_name.name, name, sizeof(rstat_name.name)); 420 res = ioctl(fd, RNDGETESTNAME, &rstat_name); 421 if (res < 0) 422 err(1, "ioctl(RNDGETESTNAME)"); 423 printf(HEADER); 424 printf("%-16s %10u %-4s %s\n", 425 rstat_name.source.rt.name, 426 rstat_name.source.rt.total, 427 find_name(rstat_name.source.rt.type), 428 strflags(rstat_name.source.rt.flags)); 429 if (vflag) { 430 printf("\tDt samples = %d\n", 431 rstat_name.source.dt_samples); 432 printf("\tDt bits = %d\n", 433 rstat_name.source.dt_total); 434 printf("\tDv samples = %d\n", 435 rstat_name.source.dv_samples); 436 printf("\tDv bits = %d\n", 437 rstat_name.source.dv_total); 438 } 439 close(fd); 440 return; 441 } 442 443 /* 444 * Run through all the devices present in the system, and either 445 * print out ones that match, or print out all of them. 446 */ 447 printf(HEADER); 448 start = 0; 449 for (;;) { 450 rstat.count = RND_MAXSTATCOUNT; 451 rstat.start = start; 452 res = ioctl(fd, RNDGETESTNUM, &rstat); 453 if (res < 0) 454 err(1, "ioctl(RNDGETESTNUM)"); 455 456 if (rstat.count == 0) 457 break; 458 459 for (i = 0; i < rstat.count; i++) { 460 if (all != 0 || 461 type == rstat.source[i].rt.type) 462 printf("%-16s %10u %-4s %s\n", 463 rstat.source[i].rt.name, 464 rstat.source[i].rt.total, 465 find_name(rstat.source[i].rt.type), 466 strflags(rstat.source[i].rt.flags)); 467 if (vflag) { 468 printf("\tDt samples = %d\n", 469 rstat.source[i].dt_samples); 470 printf("\tDt bits = %d\n", 471 rstat.source[i].dt_total); 472 printf("\tDv samples = %d\n", 473 rstat.source[i].dv_samples); 474 printf("\tDv bits = %d\n", 475 rstat.source[i].dv_total); 476 } 477 } 478 start += rstat.count; 479 } 480 481 close(fd); 482 } 483 484 static void 485 do_stats(void) 486 { 487 rndpoolstat_t rs; 488 int fd; 489 490 fd = open(_PATH_URANDOM, O_RDONLY, 0644); 491 if (fd < 0) 492 err(1, "open"); 493 494 if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0) 495 err(1, "ioctl(RNDGETPOOLSTAT)"); 496 497 printf("\t%9u bits mixed into pool\n", rs.added); 498 printf("\t%9u bits currently stored in pool (max %u)\n", 499 rs.curentropy, rs.maxentropy); 500 printf("\t%9u bits of entropy discarded due to full pool\n", 501 rs.discarded); 502 printf("\t%9u hard-random bits generated\n", rs.removed); 503 printf("\t%9u pseudo-random bits generated\n", rs.generated); 504 505 close(fd); 506 } 507 508 int 509 main(int argc, char **argv) 510 { 511 rndctl_t rctl; 512 int ch, cmd, lflag, mflag, sflag; 513 u_int32_t type; 514 char name[16]; 515 const char *filename = NULL; 516 517 if (SHA3_Selftest() != 0) 518 errx(1, "SHA-3 self-test failed"); 519 520 rctl.mask = 0; 521 rctl.flags = 0; 522 523 cmd = 0; 524 lflag = 0; 525 mflag = 0; 526 sflag = 0; 527 type = 0xff; 528 529 while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) { 530 switch (ch) { 531 case 'C': 532 rctl.flags |= RND_FLAG_NO_COLLECT; 533 rctl.mask |= RND_FLAG_NO_COLLECT; 534 mflag++; 535 break; 536 case 'E': 537 rctl.flags |= RND_FLAG_NO_ESTIMATE; 538 rctl.mask |= RND_FLAG_NO_ESTIMATE; 539 mflag++; 540 break; 541 case 'L': 542 if (cmd != 0) 543 usage(); 544 cmd = 'L'; 545 filename = optarg; 546 break; 547 case 'S': 548 if (cmd != 0) 549 usage(); 550 cmd = 'S'; 551 filename = optarg; 552 break; 553 case 'c': 554 rctl.flags &= ~RND_FLAG_NO_COLLECT; 555 rctl.mask |= RND_FLAG_NO_COLLECT; 556 mflag++; 557 break; 558 case 'e': 559 rctl.flags &= ~RND_FLAG_NO_ESTIMATE; 560 rctl.mask |= RND_FLAG_NO_ESTIMATE; 561 mflag++; 562 break; 563 case 'l': 564 lflag++; 565 break; 566 case 't': 567 if (cmd != 0) 568 usage(); 569 cmd = 't'; 570 571 type = find_type(optarg); 572 break; 573 case 'd': 574 if (cmd != 0) 575 usage(); 576 cmd = 'd'; 577 578 type = 0xff; 579 strlcpy(name, optarg, sizeof(name)); 580 break; 581 case 's': 582 sflag++; 583 break; 584 case 'v': 585 vflag++; 586 break; 587 case '?': 588 default: 589 usage(); 590 } 591 } 592 argc -= optind; 593 argv += optind; 594 595 /* 596 * No leftover non-option arguments. 597 */ 598 if (argc > 0) 599 usage(); 600 601 /* 602 * Save. 603 */ 604 if (cmd == 'S') { 605 do_save(filename, NULL, 0, 0); 606 exit(0); 607 } 608 609 /* 610 * Load. 611 */ 612 if (cmd == 'L') { 613 do_load(filename); 614 exit(0); 615 } 616 617 /* 618 * Cannot list and modify at the same time. 619 */ 620 if ((lflag != 0 || sflag != 0) && mflag != 0) 621 usage(); 622 623 /* 624 * Bomb out on no-ops. 625 */ 626 if (lflag == 0 && mflag == 0 && sflag == 0) 627 usage(); 628 629 /* 630 * If not listing, we need a device name or a type. 631 */ 632 if (lflag == 0 && cmd == 0 && sflag == 0) 633 usage(); 634 635 /* 636 * Modify request. 637 */ 638 if (mflag != 0) { 639 rctl.type = type; 640 strncpy(rctl.name, name, sizeof(rctl.name)); 641 do_ioctl(&rctl); 642 643 exit(0); 644 } 645 646 /* 647 * List sources. 648 */ 649 if (lflag != 0) 650 do_list(cmd == 0, type, name); 651 652 if (sflag != 0) 653 do_stats(); 654 655 exit(0); 656 } 657