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