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