1 /* $NetBSD: rndctl.c,v 1.30 2015/04/13 22:18:50 riastradh 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.30 2015/04/13 22:18:50 riastradh Exp $"); 37 #endif 38 39 40 #include <sys/types.h> 41 #include <sys/ioctl.h> 42 #include <sys/param.h> 43 #include <sys/rndio.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