Home | History | Annotate | Line # | Download | only in rndctl
rndctl.c revision 1.22
      1 /*	$NetBSD: rndctl.c,v 1.22 2011/11/26 01:17:17 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.22 2011/11/26 01:17:17 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 <string.h>
     52 
     53 typedef struct {
     54 	const char *a_name;
     55 	u_int32_t a_type;
     56 } arg_t;
     57 
     58 static const arg_t source_types[] = {
     59 	{ "???",     RND_TYPE_UNKNOWN },
     60 	{ "disk",    RND_TYPE_DISK },
     61 	{ "net",     RND_TYPE_NET },
     62 	{ "tape",    RND_TYPE_TAPE },
     63 	{ "tty",     RND_TYPE_TTY },
     64 	{ "rng",     RND_TYPE_RNG },
     65 	{ NULL,      0 }
     66 };
     67 
     68 __dead static void usage(void);
     69 static u_int32_t find_type(const char *name);
     70 static const char *find_name(u_int32_t);
     71 static void do_ioctl(rndctl_t *);
     72 static char * strflags(u_int32_t);
     73 static void do_list(int, u_int32_t, char *);
     74 static void do_stats(void);
     75 
     76 static void
     77 usage(void)
     78 {
     79 
     80 	fprintf(stderr, "usage: %s -CEce [-d devname | -t devtype]\n",
     81 	    getprogname());
     82 	fprintf(stderr, "       %s -ls [-d devname | -t devtype]\n",
     83 	    getprogname());
     84 	fprintf(stderr, "	%s -[L|S] save-file\n", getprogname());
     85 	exit(1);
     86 }
     87 
     88 static u_int32_t
     89 find_type(const char *name)
     90 {
     91 	const arg_t *a;
     92 
     93 	a = source_types;
     94 
     95 	while (a->a_name != NULL) {
     96 		if (strcmp(a->a_name, name) == 0)
     97 			return (a->a_type);
     98 		a++;
     99 	}
    100 
    101 	errx(1, "device name %s unknown", name);
    102 	return (0);
    103 }
    104 
    105 static const char *
    106 find_name(u_int32_t type)
    107 {
    108 	const arg_t *a;
    109 
    110 	a = source_types;
    111 
    112 	while (a->a_name != NULL) {
    113 		if (type == a->a_type)
    114 			return (a->a_name);
    115 		a++;
    116 	}
    117 
    118 	warnx("device type %u unknown", type);
    119 	return ("???");
    120 }
    121 
    122 static void
    123 do_save(const char *const filename)
    124 {
    125 	int est1, est2;
    126 	rndpoolstat_t rp;
    127 	rndsave_t rs;
    128 	SHA1_CTX s;
    129 
    130 	int fd;
    131 
    132 	fd = open("/dev/urandom", O_RDONLY, 0644);
    133 	if (fd < 0) {
    134 		err(1, "device open");
    135 	}
    136 
    137 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
    138 		err(1, "ioctl(RNDGETPOOLSTAT)");
    139 	}
    140 
    141 	est1 = rp.curentropy;
    142 
    143 	if (read(fd, rs.data, sizeof(rs.data)) != sizeof(rs.data)) {
    144 		err(1, "entropy read");
    145 	}
    146 
    147 	if (ioctl(fd, RNDGETPOOLSTAT, &rp) < 0) {
    148 		err(1, "ioctl(RNDGETPOOLSTAT)");
    149 	}
    150 
    151 	est2 = rp.curentropy;
    152 
    153 	if (est1 - est2 < 0) {
    154 		rs.entropy = 0;
    155 	} else {
    156 		rs.entropy = est1 - est2;
    157 	}
    158 
    159 	SHA1Init(&s);
    160 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
    161 	SHA1Update(&s, rs.data, sizeof(rs.data));
    162 	SHA1Final(rs.digest, &s);
    163 
    164 	close(fd);
    165 	unlink(filename);
    166 	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
    167 	if (fd < 0) {
    168 		err(1, "output open");
    169 	}
    170 
    171 	if (write(fd, &rs, sizeof(rs)) != sizeof(rs)) {
    172 		unlink(filename);
    173 		fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
    174 		err(1, "write");
    175 	}
    176 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
    177 	close(fd);
    178 }
    179 
    180 static void
    181 do_load(const char *const filename)
    182 {
    183 	int fd;
    184 	rndsave_t rs;
    185 	rnddata_t rd;
    186 	SHA1_CTX s;
    187 	uint8_t digest[SHA1_DIGEST_LENGTH];
    188 
    189 	fd = open(filename, O_RDWR, 0600);
    190 	if (fd < 0) {
    191 		err(1, "input open");
    192 	}
    193 
    194 	unlink(filename);
    195 
    196 	if (read(fd, &rs, sizeof(rs)) != sizeof(rs)) {
    197 		err(1, "read");
    198 	}
    199 
    200 	if (write(fd, &rs, sizeof(rs) != sizeof(rs))) {
    201 		err(1, "overwrite");
    202 	}
    203 	fsync_range(fd, FDATASYNC|FDISKSYNC, (off_t)0, (off_t)0);
    204 	close(fd);
    205 
    206 	SHA1Init(&s);
    207 	SHA1Update(&s, (uint8_t *)&rs.entropy, sizeof(rs.entropy));
    208 	SHA1Update(&s, rs.data, sizeof(rs.data));
    209 	SHA1Final(digest, &s);
    210 
    211 	if (memcmp(digest, rs.digest, sizeof(digest))) {
    212 		errx(1, "bad digest");
    213 	}
    214 
    215 	rd.len = MIN(sizeof(rd.data), sizeof(rs.data));
    216 	rd.entropy = rs.entropy;
    217 	memcpy(rd.data, rs.data, MIN(sizeof(rd.data), sizeof(rs.data)));
    218 
    219 	fd = open("/dev/urandom", O_RDWR, 0644);
    220 	if (fd < 0) {
    221 		err(1, "device open");
    222 	}
    223 
    224 	if (ioctl(fd, RNDADDDATA, &rd) < 0) {
    225 		err(1, "ioctl");
    226 	}
    227 	close(fd);
    228 }
    229 
    230 static void
    231 do_ioctl(rndctl_t *rctl)
    232 {
    233 	int fd;
    234 	int res;
    235 
    236 	fd = open("/dev/urandom", O_RDONLY, 0644);
    237 	if (fd < 0)
    238 		err(1, "open");
    239 
    240 	res = ioctl(fd, RNDCTL, rctl);
    241 	if (res < 0)
    242 		err(1, "ioctl(RNDCTL)");
    243 
    244 	close(fd);
    245 }
    246 
    247 static char *
    248 strflags(u_int32_t fl)
    249 {
    250 	static char str[512];
    251 
    252 	str[0] = 0;
    253 	if (fl & RND_FLAG_NO_ESTIMATE)
    254 		;
    255 	else
    256 		strlcat(str, "estimate", sizeof(str));
    257 
    258 	if (fl & RND_FLAG_NO_COLLECT)
    259 		;
    260 	else {
    261 		if (str[0])
    262 			strlcat(str, ", ", sizeof(str));
    263 		strlcat(str, "collect", sizeof(str));
    264 	}
    265 
    266 	return (str);
    267 }
    268 
    269 #define HEADER "Source                 Bits Type      Flags\n"
    270 
    271 static void
    272 do_list(int all, u_int32_t type, char *name)
    273 {
    274 	rndstat_t rstat;
    275 	rndstat_name_t rstat_name;
    276 	int fd;
    277 	int res;
    278 	uint32_t i;
    279 	u_int32_t start;
    280 
    281 	fd = open("/dev/urandom", O_RDONLY, 0644);
    282 	if (fd < 0)
    283 		err(1, "open");
    284 
    285 	if (all == 0 && type == 0xff) {
    286 		strncpy(rstat_name.name, name, sizeof(rstat_name.name));
    287 		res = ioctl(fd, RNDGETSRCNAME, &rstat_name);
    288 		if (res < 0)
    289 			err(1, "ioctl(RNDGETSRCNAME)");
    290 		printf(HEADER);
    291 		printf("%-16s %10u %-4s %s\n",
    292 		    rstat_name.source.name,
    293 		    rstat_name.source.total,
    294 		    find_name(rstat_name.source.type),
    295 		    strflags(rstat_name.source.flags));
    296 		close(fd);
    297 		return;
    298 	}
    299 
    300 	/*
    301 	 * Run through all the devices present in the system, and either
    302 	 * print out ones that match, or print out all of them.
    303 	 */
    304 	printf(HEADER);
    305 	start = 0;
    306 	for (;;) {
    307 		rstat.count = RND_MAXSTATCOUNT;
    308 		rstat.start = start;
    309 		res = ioctl(fd, RNDGETSRCNUM, &rstat);
    310 		if (res < 0)
    311 			err(1, "ioctl(RNDGETSRCNUM)");
    312 
    313 		if (rstat.count == 0)
    314 			break;
    315 
    316 		for (i = 0; i < rstat.count; i++) {
    317 			if (all != 0 ||
    318 			    type == rstat.source[i].type)
    319 				printf("%-16s %10u %-4s %s\n",
    320 				    rstat.source[i].name,
    321 				    rstat.source[i].total,
    322 				    find_name(rstat.source[i].type),
    323 				    strflags(rstat.source[i].flags));
    324 		}
    325 		start += rstat.count;
    326 	}
    327 
    328 	close(fd);
    329 }
    330 
    331 static void
    332 do_stats(void)
    333 {
    334 	rndpoolstat_t rs;
    335 	int fd;
    336 
    337 	fd = open("/dev/urandom", O_RDONLY, 0644);
    338 	if (fd < 0)
    339 		err(1, "open");
    340 
    341 	if (ioctl(fd, RNDGETPOOLSTAT, &rs) < 0)
    342 		err(1, "ioctl(RNDGETPOOLSTAT)");
    343 
    344 	printf("\t%9u bits mixed into pool\n", rs.added);
    345 	printf("\t%9u bits currently stored in pool (max %u)\n",
    346 	    rs.curentropy, rs.maxentropy);
    347 	printf("\t%9u bits of entropy discarded due to full pool\n",
    348 	    rs.discarded);
    349 	printf("\t%9u hard-random bits generated\n", rs.removed);
    350 	printf("\t%9u pseudo-random bits generated\n", rs.generated);
    351 
    352 	close(fd);
    353 }
    354 
    355 int
    356 main(int argc, char **argv)
    357 {
    358 	rndctl_t rctl;
    359 	int ch, cmd, lflag, mflag, sflag;
    360 	u_int32_t type;
    361 	char name[16];
    362 	const char *filename = NULL;
    363 
    364 	rctl.mask = 0;
    365 	rctl.flags = 0;
    366 
    367 	cmd = 0;
    368 	lflag = 0;
    369 	mflag = 0;
    370 	sflag = 0;
    371 	type = 0xff;
    372 
    373 	while ((ch = getopt(argc, argv, "CES:L:celt:d:s")) != -1) {
    374 		switch (ch) {
    375 		case 'C':
    376 			rctl.flags |= RND_FLAG_NO_COLLECT;
    377 			rctl.mask |= RND_FLAG_NO_COLLECT;
    378 			mflag++;
    379 			break;
    380 		case 'E':
    381 			rctl.flags |= RND_FLAG_NO_ESTIMATE;
    382 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    383 			mflag++;
    384 			break;
    385 		case 'L':
    386 			if (cmd != 0)
    387 				usage();
    388 			cmd = 'L';
    389 			filename = optarg;
    390 			break;
    391 		case 'S':
    392 			if (cmd != 0)
    393 				usage();
    394 			cmd = 'S';
    395 			filename = optarg;
    396 			break;
    397 		case 'c':
    398 			rctl.flags &= ~RND_FLAG_NO_COLLECT;
    399 			rctl.mask |= RND_FLAG_NO_COLLECT;
    400 			mflag++;
    401 			break;
    402 		case 'e':
    403 			rctl.flags &= ~RND_FLAG_NO_ESTIMATE;
    404 			rctl.mask |= RND_FLAG_NO_ESTIMATE;
    405 			mflag++;
    406 			break;
    407 		case 'l':
    408 			lflag++;
    409 			break;
    410 		case 't':
    411 			if (cmd != 0)
    412 				usage();
    413 			cmd = 't';
    414 
    415 			type = find_type(optarg);
    416 			break;
    417 		case 'd':
    418 			if (cmd != 0)
    419 				usage();
    420 			cmd = 'd';
    421 
    422 			type = 0xff;
    423 			strlcpy(name, optarg, sizeof(name));
    424 			break;
    425 		case 's':
    426 			sflag++;
    427 			break;
    428 		case '?':
    429 		default:
    430 			usage();
    431 		}
    432 	}
    433 	argc -= optind;
    434 	argv += optind;
    435 
    436 	/*
    437 	 * No leftover non-option arguments.
    438 	 */
    439 	if (argc > 0)
    440 		usage();
    441 
    442 	/*
    443 	 * Save.
    444 	 */
    445 	if (cmd == 'S') {
    446 		do_save(filename);
    447 		exit(0);
    448 	}
    449 
    450 	/*
    451 	 * Load.
    452 	 */
    453 	if (cmd == 'L') {
    454 		do_load(filename);
    455 		exit(0);
    456 	}
    457 
    458 	/*
    459 	 * Cannot list and modify at the same time.
    460 	 */
    461 	if ((lflag != 0 || sflag != 0) && mflag != 0)
    462 		usage();
    463 
    464 	/*
    465 	 * Bomb out on no-ops.
    466 	 */
    467 	if (lflag == 0 && mflag == 0 && sflag == 0)
    468 		usage();
    469 
    470 	/*
    471 	 * If not listing, we need a device name or a type.
    472 	 */
    473 	if (lflag == 0 && cmd == 0 && sflag == 0)
    474 		usage();
    475 
    476 	/*
    477 	 * Modify request.
    478 	 */
    479 	if (mflag != 0) {
    480 		rctl.type = type;
    481 		strncpy(rctl.name, name, sizeof(rctl.name));
    482 		do_ioctl(&rctl);
    483 
    484 		exit(0);
    485 	}
    486 
    487 	/*
    488 	 * List sources.
    489 	 */
    490 	if (lflag != 0)
    491 		do_list(cmd == 0, type, name);
    492 
    493 	if (sflag != 0)
    494 		do_stats();
    495 
    496 	exit(0);
    497 }
    498