xref: /netbsd-src/sbin/rndctl/rndctl.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
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