1 /* $NetBSD: rndctl.c,v 1.27 2014/01/15 15:05:27 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.27 2014/01/15 15:05:27 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 <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 (pwrite(fd, &rszero, sizeof(rszero), (off_t)0) != sizeof(rszero)) 207 err(1, "overwrite"); 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(_PATH_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 235 static void 236 do_ioctl(rndctl_t *rctl) 237 { 238 int fd; 239 int res; 240 241 fd = open(_PATH_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 252 static char * 253 strflags(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 276 static void 277 do_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(_PATH_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 336 static void 337 do_stats(void) 338 { 339 rndpoolstat_t rs; 340 int fd; 341 342 fd = open(_PATH_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 360 int 361 main(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