Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.27.2.1
      1 /*	$NetBSD: rndctl.c,v 1.27.2.1 2014/04/07 02:49:52 tls 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.2.1 2014/04/07 02:49:52 tls 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 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 -ls [-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 			printf("\tLZ bytes in = %d\n",
    322 			       rstat_name.source.lzv_bytes);
    323 			printf("\tLZ bits out = %d\n",
    324 			       rstat_name.source.lzv_total);
    325 		}
    326 		close(fd);
    327 		return;
    328 	}
    329 
    330 	/*
    331 	 * Run through all the devices present in the system, and either
    332 	 * print out ones that match, or print out all of them.
    333 	 */
    334 	printf(HEADER);
    335 	start = 0;
    336 	for (;;) {
    337 		rstat.count = RND_MAXSTATCOUNT;
    338 		rstat.start = start;
    339 		res = ioctl(fd, RNDGETESTNUM, &rstat);
    340 		if (res < 0)
    341 			err(1, "ioctl(RNDGETESTNUM)");
    342 
    343 		if (rstat.count == 0)
    344 			break;
    345 
    346 		for (i = 0; i < rstat.count; i++) {
    347 			if (all != 0 ||
    348 			    type == rstat.source[i].rt.type)
    349 				printf("%-16s %10u %-4s %s\n",
    350 				    rstat.source[i].rt.name,
    351 				    rstat.source[i].rt.total,
    352 				    find_name(rstat.source[i].rt.type),
    353 				    strflags(rstat.source[i].rt.flags));
    354 			if (vflag) {
    355 				printf("\tDt samples = %d\n",
    356 				       rstat.source[i].dt_samples);
    357 				printf("\tDt bits = %d\n",
    358 				       rstat.source[i].dt_total);
    359 				printf("\tDv samples = %d\n",
    360 				       rstat.source[i].dv_samples);
    361 				printf("\tDv bits = %d\n",
    362 				       rstat.source[i].dv_total);
    363 				printf("\tLZ bytes in = %d\n",
    364 				       rstat.source[i].lzv_bytes);
    365 				printf("\tLZ bits out = %d\n",
    366 				       rstat.source[i].lzv_total);
    367 			}
    368                 }
    369 		start += rstat.count;
    370 	}
    371 
    372 	close(fd);
    373 }
    374 
    375 static void
    376 do_stats(void)
    377 {
    378 	rndpoolstat_t rs;
    379 	int fd;
    380 
    381 	fd = open(_PATH_URANDOM, O_RDONLY, 0644);
    382 	if (fd < 0)
    383 		err(1, "open");
    384 
    385 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
    386 		err(1, "ioctl(RNDGETPOOLSTAT)");
    387 
    388 	printf("\t%9u bits mixed into pool\n", rs.added);
    389 	printf("\t%9u bits currently stored in pool (max %u)\n",
    390 	    rs.curentropy, rs.maxentropy);
    391 	printf("\t%9u bits of entropy discarded due to full pool\n",
    392 	    rs.discarded);
    393 	printf("\t%9u hard-random bits generated\n", rs.removed);
    394 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
    395 
    396 	close(fd);
    397 }
    398 
    399 int
    400 main(int argc, char **argv)
    401 {
    402 	rndctl_t rctl;
    403 	int ch, cmd, lflag, mflag, sflag;
    404 	u_int32_t type;
    405 	char name[16];
    406 	const char *filename = NULL;
    407 
    408 	rctl.mask = 0;
    409 	rctl.flags = 0;
    410 
    411 	cmd = 0;
    412 	lflag = 0;
    413 	mflag = 0;
    414 	sflag = 0;
    415 	type = 0xff;
    416 
    417 	while ((ch = getopt(argc, argv, "CES:L:celt:d:sv")) != -1) {
    418 		switch (ch) {
    419 		case 'C':
    420 			rctl.flags |= RND_FLAG_NO_COLLECT;
    421 			rctl.mask |= RND_FLAG_NO_COLLECT;
    422 			mflag++;
    423 			break;
    424 		case 'E':
    425 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
    426 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    427 			mflag++;
    428 			break;
    429 		case 'L':
    430 			if (cmd != 0)
    431 				usage();
    432 			cmd = 'L';
    433 			filename = optarg;
    434 			break;
    435 		case 'S':
    436 			if (cmd != 0)
    437 				usage();
    438 			cmd = 'S';
    439 			filename = optarg;
    440 			break;
    441 		case 'c':
    442 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
    443 			rctl.mask |= RND_FLAG_NO_COLLECT;
    444 			mflag++;
    445 			break;
    446 		case 'e':
    447 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
    448 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    449 			mflag++;
    450 			break;
    451 		case 'l':
    452 			lflag++;
    453 			break;
    454 		case 't':
    455 			if (cmd != 0)
    456 				usage();
    457 			cmd = 't';
    458 
    459 			type = find_type(optarg);
    460 			break;
    461 		case 'd':
    462 			if (cmd != 0)
    463 				usage();
    464 			cmd = 'd';
    465 
    466 			type = 0xff;
    467 			strlcpy(name, optarg, sizeof(name));
    468 			break;
    469 		case 's':
    470 			sflag++;
    471 			break;
    472 		case 'v':
    473 			vflag++;
    474 			break;
    475 		case '?':
    476 		default:
    477 			usage();
    478 		}
    479 	}
    480 	argc -= optind;
    481 	argv += optind;
    482 
    483 	/*
    484 	 * No leftover non-option arguments.
    485 	 */
    486 	if (argc > 0)
    487 		usage();
    488 
    489 	/*
    490 	 * Save.
    491 	 */
    492 	if (cmd == 'S') {
    493 		do_save(filename);
    494 		exit(0);
    495 	}
    496 
    497 	/*
    498 	 * Load.
    499 	 */
    500 	if (cmd == 'L') {
    501 		do_load(filename);
    502 		exit(0);
    503 	}
    504 
    505 	/*
    506 	 * Cannot list and modify at the same time.
    507 	 */
    508 	if ((lflag != 0 || sflag != 0) && mflag != 0)
    509 		usage();
    510 
    511 	/*
    512 	 * Bomb out on no-ops.
    513 	 */
    514 	if (lflag == 0 && mflag == 0 && sflag == 0)
    515 		usage();
    516 
    517 	/*
    518 	 * If not listing, we need a device name or a type.
    519 	 */
    520 	if (lflag == 0 && cmd == 0 && sflag == 0)
    521 		usage();
    522 
    523 	/*
    524 	 * Modify request.
    525 	 */
    526 	if (mflag != 0) {
    527 		rctl.type = type;
    528 		strncpy(rctl.name, name, sizeof(rctl.name));
    529 		do_ioctl(&rctl);
    530 
    531 		exit(0);
    532 	}
    533 
    534 	/*
    535 	 * List sources.
    536 	 */
    537 	if (lflag != 0)
    538 		do_list(cmd == 0, type, name);
    539 
    540 	if (sflag != 0)
    541 		do_stats();
    542 
    543 	exit(0);
    544 }
    545