rndctl.c revision 1.27.2.1 1 /* $NetBSD: rndctl.c,v 1.27.2.1 2014/04/07 02:49:52 tls 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.27.2.1 2014/04/07 02:49:52 tls 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 -ls [-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 printf("\tLZ bytes in = %d\n",
322 rstat_name.source.lzv_bytes);
323 printf("\tLZ bits out = %d\n",
324 rstat_name.source.lzv_total);
325 }
326 close(fd);
327 return;
328 }
329
330 /*
331 * Run through all the devices present in the system, and either
332 * print out ones that match, or print out all of them.
333 */
334 printf(HEADER);
335 start = 0;
336 for (;;) {
337 rstat.count = RND_MAXSTATCOUNT;
338 rstat.start = start;
339 res = ioctl(fd, RNDGETESTNUM, &rstat);
340 if (res < 0)
341 err(1, "ioctl(RNDGETESTNUM)");
342
343 if (rstat.count == 0)
344 break;
345
346 for (i = 0; i < rstat.count; i++) {
347 if (all != 0 ||
348 type == rstat.source[i].rt.type)
349 printf("%-16s %10u %-4s %s\n",
350 rstat.source[i].rt.name,
351 rstat.source[i].rt.total,
352 find_name(rstat.source[i].rt.type),
353 strflags(rstat.source[i].rt.flags));
354 if (vflag) {
355 printf("\tDt samples = %d\n",
356 rstat.source[i].dt_samples);
357 printf("\tDt bits = %d\n",
358 rstat.source[i].dt_total);
359 printf("\tDv samples = %d\n",
360 rstat.source[i].dv_samples);
361 printf("\tDv bits = %d\n",
362 rstat.source[i].dv_total);
363 printf("\tLZ bytes in = %d\n",
364 rstat.source[i].lzv_bytes);
365 printf("\tLZ bits out = %d\n",
366 rstat.source[i].lzv_total);
367 }
368 }
369 start += rstat.count;
370 }
371
372 close(fd);
373 }
374
375 static void
376 do_stats(void)
377 {
378 rndpoolstat_t rs;
379 int fd;
380
381 fd = open(_PATH_URANDOM, O_RDONLY, 0644);
382 if (fd < 0)
383 err(1, "open");
384
385 if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
386 err(1, "ioctl(RNDGETPOOLSTAT)");
387
388 printf("\t%9u bits mixed into pool\n", rs.added);
389 printf("\t%9u bits currently stored in pool (max %u)\n",
390 rs.curentropy, rs.maxentropy);
391 printf("\t%9u bits of entropy discarded due to full pool\n",
392 rs.discarded);
393 printf("\t%9u hard-random bits generated\n", rs.removed);
394 printf("\t%9u pseudo-random bits generated\n", rs.generated);
395
396 close(fd);
397 }
398
399 int
400 main(int argc, char **argv)
401 {
402 rndctl_t rctl;
403 int ch, cmd, lflag, mflag, sflag;
404 u_int32_t type;
405 char name[16];
406 const char *filename = NULL;
407
408 rctl.mask = 0;
409 rctl.flags = 0;
410
411 cmd = 0;
412 lflag = 0;
413 mflag = 0;
414 sflag = 0;
415 type = 0xff;
416
417 while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
418 switch (ch) {
419 case 'C':
420 rctl.flags |= RND_FLAG_NO_COLLECT;
421 rctl.mask |= RND_FLAG_NO_COLLECT;
422 mflag++;
423 break;
424 case 'E':
425 rctl.flags |= RND_FLAG_NO_ESTIMATE;
426 rctl.mask |= RND_FLAG_NO_ESTIMATE;
427 mflag++;
428 break;
429 case 'L':
430 if (cmd != 0)
431 usage();
432 cmd = 'L';
433 filename = optarg;
434 break;
435 case 'S':
436 if (cmd != 0)
437 usage();
438 cmd = 'S';
439 filename = optarg;
440 break;
441 case 'c':
442 rctl.flags &= ~RND_FLAG_NO_COLLECT;
443 rctl.mask |= RND_FLAG_NO_COLLECT;
444 mflag++;
445 break;
446 case 'e':
447 rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
448 rctl.mask |= RND_FLAG_NO_ESTIMATE;
449 mflag++;
450 break;
451 case 'l':
452 lflag++;
453 break;
454 case 't':
455 if (cmd != 0)
456 usage();
457 cmd = 't';
458
459 type = find_type(optarg);
460 break;
461 case 'd':
462 if (cmd != 0)
463 usage();
464 cmd = 'd';
465
466 type = 0xff;
467 strlcpy(name, optarg, sizeof(name));
468 break;
469 case 's':
470 sflag++;
471 break;
472 case 'v':
473 vflag++;
474 break;
475 case '?':
476 default:
477 usage();
478 }
479 }
480 argc -= optind;
481 argv += optind;
482
483 /*
484 * No leftover non-option arguments.
485 */
486 if (argc > 0)
487 usage();
488
489 /*
490 * Save.
491 */
492 if (cmd == 'S') {
493 do_save(filename);
494 exit(0);
495 }
496
497 /*
498 * Load.
499 */
500 if (cmd == 'L') {
501 do_load(filename);
502 exit(0);
503 }
504
505 /*
506 * Cannot list and modify at the same time.
507 */
508 if ((lflag != 0 || sflag != 0) && mflag != 0)
509 usage();
510
511 /*
512 * Bomb out on no-ops.
513 */
514 if (lflag == 0 && mflag == 0 && sflag == 0)
515 usage();
516
517 /*
518 * If not listing, we need a device name or a type.
519 */
520 if (lflag == 0 && cmd == 0 && sflag == 0)
521 usage();
522
523 /*
524 * Modify request.
525 */
526 if (mflag != 0) {
527 rctl.type = type;
528 strncpy(rctl.name, name, sizeof(rctl.name));
529 do_ioctl(&rctl);
530
531 exit(0);
532 }
533
534 /*
535 * List sources.
536 */
537 if (lflag != 0)
538 do_list(cmd == 0, type, name);
539
540 if (sflag != 0)
541 do_stats();
542
543 exit(0);
544 }
545