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