Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.29
      1 /*	$NetBSD: rndctl.c,v 1.29 2014/08/10 17:13:14 wiz 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.29 2014/08/10 17:13:14 wiz 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 [-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