xref: /netbsd-src/sbin/rndctl/rndctl.c (revision 1897181a7231d5fc7ab48994d1447fcbc4e13a49)
1 /*	$NetBSD: rndctl.c,v 1.23 2011/12/17 13:18:20 apb 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.23 2011/12/17 13:18:20 apb Exp $");
37 #endif
38 
39 
40 #include <sys/types.h>
41 #include <sys/ioctl.h>
42 #include <sys/param.h>
43 #include <sys/rnd.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 <string.h>
52 
53 typedef struct {
54 	const char *a_name;
55 	u_int32_t a_type;
56 } arg_t;
57 
58 static const arg_t source_types[] = {
59 	{ "???",     RND_TYPE_UNKNOWN },
60 	{ "disk",    RND_TYPE_DISK },
61 	{ "net",     RND_TYPE_NET },
62 	{ "tape",    RND_TYPE_TAPE },
63 	{ "tty",     RND_TYPE_TTY },
64 	{ "rng",     RND_TYPE_RNG },
65 	{ NULL,      0 }
66 };
67 
68 __dead static void usage(void);
69 static u_int32_t find_type(const char *name);
70 static const char *find_name(u_int32_t);
71 static void do_ioctl(rndctl_t *);
72 static char * strflags(u_int32_t);
73 static void do_list(int, u_int32_t, char *);
74 static void do_stats(void);
75 
76 static void
77 usage(void)
78 {
79 
80 	fprintf(stderr, "usage: %s -CEce [-d devname | -t devtype]\n",
81 	    getprogname());
82 	fprintf(stderr, "       %s -ls [-d devname | -t devtype]\n",
83 	    getprogname());
84 	fprintf(stderr, "	%s -[L|S] save-file\n", getprogname());
85 	exit(1);
86 }
87 
88 static u_int32_t
89 find_type(const char *name)
90 {
91 	const arg_t *a;
92 
93 	a = source_types;
94 
95 	while (a->a_name != NULL) {
96 		if (strcmp(a->a_name, name) == 0)
97 			return (a->a_type);
98 		a++;
99 	}
100 
101 	errx(1, "device name %s unknown", name);
102 	return (0);
103 }
104 
105 static const char *
106 find_name(u_int32_t type)
107 {
108 	const arg_t *a;
109 
110 	a = source_types;
111 
112 	while (a->a_name != NULL) {
113 		if (type == a->a_type)
114 			return (a->a_name);
115 		a++;
116 	}
117 
118 	warnx("device type %u unknown", type);
119 	return ("???");
120 }
121 
122 static void
123 do_save(const char *const filename)
124 {
125 	int est1, est2;
126 	rndpoolstat_t rp;
127 	rndsave_t rs;
128 	SHA1_CTX s;
129 
130 	int fd;
131 
132 	fd = open("/dev/urandom", O_RDONLY, 0644);
133 	if (fd < 0) {
134 		err(1, "device open");
135 	}
136 
137 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
138 		err(1, "ioctl(RNDGETPOOLSTAT)");
139 	}
140 
141 	est1 = rp.curentropy;
142 
143 	if (read(fd, rs.data, sizeof(rs.data)) != sizeof(rs.data)) {
144 		err(1, "entropy read");
145 	}
146 
147 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
148 		err(1, "ioctl(RNDGETPOOLSTAT)");
149 	}
150 
151 	est2 = rp.curentropy;
152 
153 	if (est1 - est2 < 0) {
154 		rs.entropy = 0;
155 	} else {
156 		rs.entropy = est1 - est2;
157 	}
158 
159 	SHA1Init(&s);
160 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
161 	SHA1Update(&s, rs.data, sizeof(rs.data));
162 	SHA1Final(rs.digest, &s);
163 
164 	close(fd);
165 	unlink(filename);
166 	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
167 	if (fd < 0) {
168 		err(1, "output open");
169 	}
170 
171 	if (write(fd, &rs, sizeof(rs)) != sizeof(rs)) {
172 		unlink(filename);
173 		fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
174 		err(1, "write");
175 	}
176 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
177 	close(fd);
178 }
179 
180 static void
181 do_load(const char *const filename)
182 {
183 	int fd;
184 	rndsave_t rs, rszero;
185 	rnddata_t rd;
186 	SHA1_CTX s;
187 	uint8_t digest[SHA1_DIGEST_LENGTH];
188 
189 	fd = open(filename, O_RDWR, 0600);
190 	if (fd < 0) {
191 		err(1, "input open");
192 	}
193 
194 	unlink(filename);
195 
196 	if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) {
197 		err(1, "read");
198 	}
199 
200 	memset(&rszero, 0, sizeof(rszero));
201 	if (write(fd, &rszero, sizeof(rszero) != sizeof(rszero))) {
202 		err(1, "overwrite");
203 	}
204 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
205 	close(fd);
206 
207 	SHA1Init(&s);
208 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
209 	SHA1Update(&s, rs.data, sizeof(rs.data));
210 	SHA1Final(digest, &s);
211 
212 	if (memcmp(digest, rs.digest, sizeof(digest))) {
213 		errx(1, "bad digest");
214 	}
215 
216 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
217 	rd.entropy = rs.entropy;
218 	memcpy(rd.data, rs.data, MIN(sizeof(rd.data), sizeof(rs.data)));
219 
220 	fd = open("/dev/urandom", O_RDWR, 0644);
221 	if (fd < 0) {
222 		err(1, "device open");
223 	}
224 
225 	if (ioctl(fd, RNDADDDATA, &rd) < 0) {
226 		err(1, "ioctl");
227 	}
228 	close(fd);
229 }
230 
231 static void
232 do_ioctl(rndctl_t *rctl)
233 {
234 	int fd;
235 	int res;
236 
237 	fd = open("/dev/urandom", O_RDONLY, 0644);
238 	if (fd < 0)
239 		err(1, "open");
240 
241 	res = ioctl(fd, RNDCTL, rctl);
242 	if (res < 0)
243 		err(1, "ioctl(RNDCTL)");
244 
245 	close(fd);
246 }
247 
248 static char *
249 strflags(u_int32_t fl)
250 {
251 	static char str[512];
252 
253 	str[0] = 0;
254 	if (fl & RND_FLAG_NO_ESTIMATE)
255 		;
256 	else
257 		strlcat(str, "estimate", sizeof(str));
258 
259 	if (fl & RND_FLAG_NO_COLLECT)
260 		;
261 	else {
262 		if (str[0])
263 			strlcat(str, ", ", sizeof(str));
264 		strlcat(str, "collect", sizeof(str));
265 	}
266 
267 	return (str);
268 }
269 
270 #define HEADER "Source                 Bits Type      Flags\n"
271 
272 static void
273 do_list(int all, u_int32_t type, char *name)
274 {
275 	rndstat_t rstat;
276 	rndstat_name_t rstat_name;
277 	int fd;
278 	int res;
279 	uint32_t i;
280 	u_int32_t start;
281 
282 	fd = open("/dev/urandom", O_RDONLY, 0644);
283 	if (fd < 0)
284 		err(1, "open");
285 
286 	if (all == 0 && type == 0xff) {
287 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
288 		res = ioctl(fd, RNDGETSRCNAME, &rstat_name);
289 		if (res < 0)
290 			err(1, "ioctl(RNDGETSRCNAME)");
291 		printf(HEADER);
292 		printf("%-16s %10u %-4s %s\n",
293 		    rstat_name.source.name,
294 		    rstat_name.source.total,
295 		    find_name(rstat_name.source.type),
296 		    strflags(rstat_name.source.flags));
297 		close(fd);
298 		return;
299 	}
300 
301 	/*
302 	 * Run through all the devices present in the system, and either
303 	 * print out ones that match, or print out all of them.
304 	 */
305 	printf(HEADER);
306 	start = 0;
307 	for (;;) {
308 		rstat.count = RND_MAXSTATCOUNT;
309 		rstat.start = start;
310 		res = ioctl(fd, RNDGETSRCNUM, &rstat);
311 		if (res < 0)
312 			err(1, "ioctl(RNDGETSRCNUM)");
313 
314 		if (rstat.count == 0)
315 			break;
316 
317 		for (i = 0; i < rstat.count; i++) {
318 			if (all != 0 ||
319 			    type == rstat.source[i].type)
320 				printf("%-16s %10u %-4s %s\n",
321 				    rstat.source[i].name,
322 				    rstat.source[i].total,
323 				    find_name(rstat.source[i].type),
324 				    strflags(rstat.source[i].flags));
325 		}
326 		start += rstat.count;
327 	}
328 
329 	close(fd);
330 }
331 
332 static void
333 do_stats(void)
334 {
335 	rndpoolstat_t rs;
336 	int fd;
337 
338 	fd = open("/dev/urandom", O_RDONLY, 0644);
339 	if (fd < 0)
340 		err(1, "open");
341 
342 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
343 		err(1, "ioctl(RNDGETPOOLSTAT)");
344 
345 	printf("\t%9u bits mixed into pool\n", rs.added);
346 	printf("\t%9u bits currently stored in pool (max %u)\n",
347 	    rs.curentropy, rs.maxentropy);
348 	printf("\t%9u bits of entropy discarded due to full pool\n",
349 	    rs.discarded);
350 	printf("\t%9u hard-random bits generated\n", rs.removed);
351 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
352 
353 	close(fd);
354 }
355 
356 int
357 main(int argc, char **argv)
358 {
359 	rndctl_t rctl;
360 	int ch, cmd, lflag, mflag, sflag;
361 	u_int32_t type;
362 	char name[16];
363 	const char *filename = NULL;
364 
365 	rctl.mask = 0;
366 	rctl.flags = 0;
367 
368 	cmd = 0;
369 	lflag = 0;
370 	mflag = 0;
371 	sflag = 0;
372 	type = 0xff;
373 
374 	while ((ch = getopt(argc, argv, "CES:L:celt:d:s")) != -1) {
375 		switch (ch) {
376 		case 'C':
377 			rctl.flags |= RND_FLAG_NO_COLLECT;
378 			rctl.mask |= RND_FLAG_NO_COLLECT;
379 			mflag++;
380 			break;
381 		case 'E':
382 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
383 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
384 			mflag++;
385 			break;
386 		case 'L':
387 			if (cmd != 0)
388 				usage();
389 			cmd = 'L';
390 			filename = optarg;
391 			break;
392 		case 'S':
393 			if (cmd != 0)
394 				usage();
395 			cmd = 'S';
396 			filename = optarg;
397 			break;
398 		case 'c':
399 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
400 			rctl.mask |= RND_FLAG_NO_COLLECT;
401 			mflag++;
402 			break;
403 		case 'e':
404 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
405 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
406 			mflag++;
407 			break;
408 		case 'l':
409 			lflag++;
410 			break;
411 		case 't':
412 			if (cmd != 0)
413 				usage();
414 			cmd = 't';
415 
416 			type = find_type(optarg);
417 			break;
418 		case 'd':
419 			if (cmd != 0)
420 				usage();
421 			cmd = 'd';
422 
423 			type = 0xff;
424 			strlcpy(name, optarg, sizeof(name));
425 			break;
426 		case 's':
427 			sflag++;
428 			break;
429 		case '?':
430 		default:
431 			usage();
432 		}
433 	}
434 	argc -= optind;
435 	argv += optind;
436 
437 	/*
438 	 * No leftover non-option arguments.
439 	 */
440 	if (argc > 0)
441 		usage();
442 
443 	/*
444 	 * Save.
445 	 */
446 	if (cmd == 'S') {
447 		do_save(filename);
448 		exit(0);
449 	}
450 
451 	/*
452 	 * Load.
453 	 */
454 	if (cmd == 'L') {
455 		do_load(filename);
456 		exit(0);
457 	}
458 
459 	/*
460 	 * Cannot list and modify at the same time.
461 	 */
462 	if ((lflag != 0 || sflag != 0) && mflag != 0)
463 		usage();
464 
465 	/*
466 	 * Bomb out on no-ops.
467 	 */
468 	if (lflag == 0 && mflag == 0 && sflag == 0)
469 		usage();
470 
471 	/*
472 	 * If not listing, we need a device name or a type.
473 	 */
474 	if (lflag == 0 && cmd == 0 && sflag == 0)
475 		usage();
476 
477 	/*
478 	 * Modify request.
479 	 */
480 	if (mflag != 0) {
481 		rctl.type = type;
482 		strncpy(rctl.name, name, sizeof(rctl.name));
483 		do_ioctl(&rctl);
484 
485 		exit(0);
486 	}
487 
488 	/*
489 	 * List sources.
490 	 */
491 	if (lflag != 0)
492 		do_list(cmd == 0, type, name);
493 
494 	if (sflag != 0)
495 		do_stats();
496 
497 	exit(0);
498 }
499