1 /* $NetBSD: rndctl.c,v 1.25 2012/08/14 14:41:07 jruoho 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.25 2012/08/14 14:41:07 jruoho 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