xref: /netbsd/sbin/rndctl/rndctl.c (revision a4130fcc)
1 /*	$NetBSD: rndctl.c,v 1.30 2015/04/13 22:18:50 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.30 2015/04/13 22:18:50 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 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <err.h>
51 #include <paths.h>
52 #include <string.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 *const filename)
131 {
132 	int est1, est2;
133 	rndpoolstat_t rp;
134 	rndsave_t rs;
135 	SHA1_CTX s;
136 
137 	int fd;
138 
139 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
140 	if (fd < 0) {
141 		err(1, "device open");
142 	}
143 
144 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
145 		err(1, "ioctl(RNDGETPOOLSTAT)");
146 	}
147 
148 	est1 = rp.curentropy;
149 
150 	if (read(fd, rs.data, sizeof(rs.data)) != sizeof(rs.data)) {
151 		err(1, "entropy read");
152 	}
153 
154 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
155 		err(1, "ioctl(RNDGETPOOLSTAT)");
156 	}
157 
158 	est2 = rp.curentropy;
159 
160 	if (est1 - est2 < 0) {
161 		rs.entropy = 0;
162 	} else {
163 		rs.entropy = est1 - est2;
164 	}
165 
166 	SHA1Init(&s);
167 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
168 	SHA1Update(&s, rs.data, sizeof(rs.data));
169 	SHA1Final(rs.digest, &s);
170 
171 	close(fd);
172 	unlink(filename);
173 	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
174 	if (fd < 0) {
175 		err(1, "output open");
176 	}
177 
178 	if (write(fd, &rs, sizeof(rs)) != sizeof(rs)) {
179 		unlink(filename);
180 		fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
181 		err(1, "write");
182 	}
183 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
184 	close(fd);
185 }
186 
187 static void
188 do_load(const char *const filename)
189 {
190 	int fd;
191 	rndsave_t rs, rszero;
192 	rnddata_t rd;
193 	SHA1_CTX s;
194 	uint8_t digest[SHA1_DIGEST_LENGTH];
195 
196 	fd = open(filename, O_RDWR, 0600);
197 	if (fd < 0) {
198 		err(1, "input open");
199 	}
200 
201 	unlink(filename);
202 
203 	if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) {
204 		err(1, "read");
205 	}
206 
207 	memset(&rszero, 0, sizeof(rszero));
208 	if (pwrite(fd, &rszero, sizeof(rszero), (off_t)0) != sizeof(rszero))
209 		err(1, "overwrite");
210 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
211 	close(fd);
212 
213 	SHA1Init(&s);
214 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
215 	SHA1Update(&s, rs.data, sizeof(rs.data));
216 	SHA1Final(digest, &s);
217 
218 	if (memcmp(digest, rs.digest, sizeof(digest))) {
219 		errx(1, "bad digest");
220 	}
221 
222 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
223 	rd.entropy = rs.entropy;
224 	memcpy(rd.data, rs.data, MIN(sizeof(rd.data), sizeof(rs.data)));
225 
226 	fd = open(_PATH_URANDOM, O_RDWR, 0644);
227 	if (fd < 0) {
228 		err(1, "device open");
229 	}
230 
231 	if (ioctl(fd, RNDADDDATA, &rd) < 0) {
232 		err(1, "ioctl");
233 	}
234 	close(fd);
235 }
236 
237 static void
238 do_ioctl(rndctl_t *rctl)
239 {
240 	int fd;
241 	int res;
242 
243 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
244 	if (fd < 0)
245 		err(1, "open");
246 
247 	res = ioctl(fd, RNDCTL, rctl);
248 	if (res < 0)
249 		err(1, "ioctl(RNDCTL)");
250 
251 	close(fd);
252 }
253 
254 static char *
255 strflags(u_int32_t fl)
256 {
257 	static char str[512];
258 
259 	str[0] = '\0';
260 	if (fl & RND_FLAG_NO_ESTIMATE)
261 		;
262 	else
263 		strlcat(str, "estimate, ", sizeof(str));
264 
265 	if (fl & RND_FLAG_NO_COLLECT)
266 		;
267 	else
268 		strlcat(str, "collect, ", sizeof(str));
269 
270 	if (fl & RND_FLAG_COLLECT_VALUE)
271 		strlcat(str, "v, ", sizeof(str));
272 	if (fl & RND_FLAG_COLLECT_TIME)
273 		strlcat(str, "t, ", sizeof(str));
274 	if (fl & RND_FLAG_ESTIMATE_VALUE)
275 		strlcat(str, "dv, ", sizeof(str));
276 	if (fl & RND_FLAG_ESTIMATE_TIME)
277 		strlcat(str, "dt, ", sizeof(str));
278 
279 	if (str[strlen(str) - 2] == ',')
280 		str[strlen(str) - 2] = '\0';
281 
282 	return (str);
283 }
284 
285 #define HEADER "Source                 Bits Type      Flags\n"
286 
287 static void
288 do_list(int all, u_int32_t type, char *name)
289 {
290 	rndstat_est_t rstat;
291 	rndstat_est_name_t rstat_name;
292 	int fd;
293 	int res;
294 	uint32_t i;
295 	u_int32_t start;
296 
297 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
298 	if (fd < 0)
299 		err(1, "open");
300 
301 	if (all == 0 && type == 0xff) {
302 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
303 		res = ioctl(fd, RNDGETESTNAME, &rstat_name);
304 		if (res < 0)
305 			err(1, "ioctl(RNDGETESTNAME)");
306 		printf(HEADER);
307 		printf("%-16s %10u %-4s %s\n",
308 		    rstat_name.source.rt.name,
309 		    rstat_name.source.rt.total,
310 		    find_name(rstat_name.source.rt.type),
311 		    strflags(rstat_name.source.rt.flags));
312 		if (vflag) {
313 			printf("\tDt samples = %d\n",
314 			       rstat_name.source.dt_samples);
315 			printf("\tDt bits = %d\n",
316 			       rstat_name.source.dt_total);
317 			printf("\tDv samples = %d\n",
318 				rstat_name.source.dv_samples);
319 			printf("\tDv bits = %d\n",
320 			       rstat_name.source.dv_total);
321 		}
322 		close(fd);
323 		return;
324 	}
325 
326 	/*
327 	 * Run through all the devices present in the system, and either
328 	 * print out ones that match, or print out all of them.
329 	 */
330 	printf(HEADER);
331 	start = 0;
332 	for (;;) {
333 		rstat.count = RND_MAXSTATCOUNT;
334 		rstat.start = start;
335 		res = ioctl(fd, RNDGETESTNUM, &rstat);
336 		if (res < 0)
337 			err(1, "ioctl(RNDGETESTNUM)");
338 
339 		if (rstat.count == 0)
340 			break;
341 
342 		for (i = 0; i < rstat.count; i++) {
343 			if (all != 0 ||
344 			    type == rstat.source[i].rt.type)
345 				printf("%-16s %10u %-4s %s\n",
346 				    rstat.source[i].rt.name,
347 				    rstat.source[i].rt.total,
348 				    find_name(rstat.source[i].rt.type),
349 				    strflags(rstat.source[i].rt.flags));
350 			if (vflag) {
351 				printf("\tDt samples = %d\n",
352 				       rstat.source[i].dt_samples);
353 				printf("\tDt bits = %d\n",
354 				       rstat.source[i].dt_total);
355 				printf("\tDv samples = %d\n",
356 				       rstat.source[i].dv_samples);
357 				printf("\tDv bits = %d\n",
358 				       rstat.source[i].dv_total);
359 			}
360                 }
361 		start += rstat.count;
362 	}
363 
364 	close(fd);
365 }
366 
367 static void
368 do_stats(void)
369 {
370 	rndpoolstat_t rs;
371 	int fd;
372 
373 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
374 	if (fd < 0)
375 		err(1, "open");
376 
377 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
378 		err(1, "ioctl(RNDGETPOOLSTAT)");
379 
380 	printf("\t%9u bits mixed into pool\n", rs.added);
381 	printf("\t%9u bits currently stored in pool (max %u)\n",
382 	    rs.curentropy, rs.maxentropy);
383 	printf("\t%9u bits of entropy discarded due to full pool\n",
384 	    rs.discarded);
385 	printf("\t%9u hard-random bits generated\n", rs.removed);
386 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
387 
388 	close(fd);
389 }
390 
391 int
392 main(int argc, char **argv)
393 {
394 	rndctl_t rctl;
395 	int ch, cmd, lflag, mflag, sflag;
396 	u_int32_t type;
397 	char name[16];
398 	const char *filename = NULL;
399 
400 	rctl.mask = 0;
401 	rctl.flags = 0;
402 
403 	cmd = 0;
404 	lflag = 0;
405 	mflag = 0;
406 	sflag = 0;
407 	type = 0xff;
408 
409 	while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
410 		switch (ch) {
411 		case 'C':
412 			rctl.flags |= RND_FLAG_NO_COLLECT;
413 			rctl.mask |= RND_FLAG_NO_COLLECT;
414 			mflag++;
415 			break;
416 		case 'E':
417 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
418 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
419 			mflag++;
420 			break;
421 		case 'L':
422 			if (cmd != 0)
423 				usage();
424 			cmd = 'L';
425 			filename = optarg;
426 			break;
427 		case 'S':
428 			if (cmd != 0)
429 				usage();
430 			cmd = 'S';
431 			filename = optarg;
432 			break;
433 		case 'c':
434 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
435 			rctl.mask |= RND_FLAG_NO_COLLECT;
436 			mflag++;
437 			break;
438 		case 'e':
439 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
440 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
441 			mflag++;
442 			break;
443 		case 'l':
444 			lflag++;
445 			break;
446 		case 't':
447 			if (cmd != 0)
448 				usage();
449 			cmd = 't';
450 
451 			type = find_type(optarg);
452 			break;
453 		case 'd':
454 			if (cmd != 0)
455 				usage();
456 			cmd = 'd';
457 
458 			type = 0xff;
459 			strlcpy(name, optarg, sizeof(name));
460 			break;
461 		case 's':
462 			sflag++;
463 			break;
464 		case 'v':
465 			vflag++;
466 			break;
467 		case '?':
468 		default:
469 			usage();
470 		}
471 	}
472 	argc -= optind;
473 	argv += optind;
474 
475 	/*
476 	 * No leftover non-option arguments.
477 	 */
478 	if (argc > 0)
479 		usage();
480 
481 	/*
482 	 * Save.
483 	 */
484 	if (cmd == 'S') {
485 		do_save(filename);
486 		exit(0);
487 	}
488 
489 	/*
490 	 * Load.
491 	 */
492 	if (cmd == 'L') {
493 		do_load(filename);
494 		exit(0);
495 	}
496 
497 	/*
498 	 * Cannot list and modify at the same time.
499 	 */
500 	if ((lflag != 0 || sflag != 0) && mflag != 0)
501 		usage();
502 
503 	/*
504 	 * Bomb out on no-ops.
505 	 */
506 	if (lflag == 0 && mflag == 0 && sflag == 0)
507 		usage();
508 
509 	/*
510 	 * If not listing, we need a device name or a type.
511 	 */
512 	if (lflag == 0 && cmd == 0 && sflag == 0)
513 		usage();
514 
515 	/*
516 	 * Modify request.
517 	 */
518 	if (mflag != 0) {
519 		rctl.type = type;
520 		strncpy(rctl.name, name, sizeof(rctl.name));
521 		do_ioctl(&rctl);
522 
523 		exit(0);
524 	}
525 
526 	/*
527 	 * List sources.
528 	 */
529 	if (lflag != 0)
530 		do_list(cmd == 0, type, name);
531 
532 	if (sflag != 0)
533 		do_stats();
534 
535 	exit(0);
536 }
537