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