rndctl.c revision 1.29 1 /* $NetBSD: rndctl.c,v 1.29 2014/08/10 17:13:14 wiz 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.29 2014/08/10 17:13:14 wiz 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 <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