rndctl.c revision 1.30.16.1 1 /* $NetBSD: rndctl.c,v 1.30.16.1 2020/04/08 14:07:20 martin 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.30.16.1 2020/04/08 14:07:20 martin 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