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