randomdev.c revision 71037
162053Smarkm/*-
262765Smarkm * Copyright (c) 2000 Mark R V Murray
362053Smarkm * All rights reserved.
462053Smarkm *
562053Smarkm * Redistribution and use in source and binary forms, with or without
662053Smarkm * modification, are permitted provided that the following conditions
762053Smarkm * are met:
862053Smarkm * 1. Redistributions of source code must retain the above copyright
962053Smarkm *    notice, this list of conditions and the following disclaimer
1062053Smarkm *    in this position and unchanged.
1162053Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1262053Smarkm *    notice, this list of conditions and the following disclaimer in the
1362053Smarkm *    documentation and/or other materials provided with the distribution.
1462053Smarkm *
1562053Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1662053Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1762053Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1862053Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1962053Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2062053Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2162053Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2262053Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2362053Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2462053Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2562053Smarkm *
2662053Smarkm * $FreeBSD: head/sys/dev/random/randomdev.c 71037 2001-01-14 17:50:15Z markm $
2762053Smarkm */
2862053Smarkm
2962053Smarkm#include <sys/param.h>
3062765Smarkm#include <sys/queue.h>
3162053Smarkm#include <sys/systm.h>
3262053Smarkm#include <sys/conf.h>
3362053Smarkm#include <sys/fcntl.h>
3462053Smarkm#include <sys/uio.h>
3562053Smarkm#include <sys/kernel.h>
3662053Smarkm#include <sys/malloc.h>
3762053Smarkm#include <sys/module.h>
3862053Smarkm#include <sys/bus.h>
3967112Smarkm#include <sys/poll.h>
4070834Swollman#include <sys/selinfo.h>
4162053Smarkm#include <sys/random.h>
4271037Smarkm#include <sys/sysctl.h>
4367112Smarkm#include <sys/vnode.h>
4462053Smarkm#include <machine/bus.h>
4562053Smarkm#include <machine/resource.h>
4662053Smarkm#include <crypto/blowfish/blowfish.h>
4762053Smarkm
4867112Smarkm#include <dev/random/hash.h>
4967112Smarkm#include <dev/random/yarrow.h>
5062053Smarkm
5163855Smarkmstatic d_open_t random_open;
5269172Smarkmstatic d_close_t random_close;
5362765Smarkmstatic d_read_t random_read;
5462765Smarkmstatic d_write_t random_write;
5565686Smarkmstatic d_ioctl_t random_ioctl;
5667112Smarkmstatic d_poll_t random_poll;
5762053Smarkm
5862053Smarkm#define CDEV_MAJOR	2
5962053Smarkm#define RANDOM_MINOR	3
6062149Smarkm#define URANDOM_MINOR	4
6162053Smarkm
6262053Smarkmstatic struct cdevsw random_cdevsw = {
6363855Smarkm	/* open */	random_open,
6469172Smarkm	/* close */	random_close,
6562765Smarkm	/* read */	random_read,
6662765Smarkm	/* write */	random_write,
6765686Smarkm	/* ioctl */	random_ioctl,
6867112Smarkm	/* poll */	random_poll,
6962053Smarkm	/* mmap */	nommap,
7062053Smarkm	/* strategy */	nostrategy,
7162053Smarkm	/* name */	"random",
7262053Smarkm	/* maj */	CDEV_MAJOR,
7362053Smarkm	/* dump */	nodump,
7462053Smarkm	/* psize */	nopsize,
7562053Smarkm	/* flags */	0,
7662053Smarkm	/* bmaj */	-1
7762053Smarkm};
7862053Smarkm
7962053Smarkm/* For use with make_dev(9)/destroy_dev(9). */
8062765Smarkmstatic dev_t random_dev;
8165686Smarkmstatic dev_t urandom_dev; /* XXX Temporary */
8262053Smarkm
8371037Smarkm/* To stash the sysctl's until they are removed */
8471037Smarkmstatic struct sysctl_oid *random_sysctl[10]; /* magic # is sysctl count */
8571037Smarkmstatic int sysctlcount = 0;
8662053Smarkm
8762053Smarkmstatic int
8863855Smarkmrandom_open(dev_t dev, int flags, int fmt, struct proc *p)
8963855Smarkm{
9063855Smarkm	if ((flags & FWRITE) && (securelevel > 0 || suser(p)))
9163855Smarkm		return EPERM;
9263855Smarkm	else
9363855Smarkm		return 0;
9463855Smarkm}
9563855Smarkm
9663855Smarkmstatic int
9769172Smarkmrandom_close(dev_t dev, int flags, int fmt, struct proc *p)
9869172Smarkm{
9969174Smarkm	if ((flags & FWRITE) && !(securelevel > 0 || suser(p)))
10069172Smarkm		random_reseed();
10169172Smarkm	return 0;
10269172Smarkm}
10369172Smarkm
10469172Smarkmstatic int
10562765Smarkmrandom_read(dev_t dev, struct uio *uio, int flag)
10662053Smarkm{
10762053Smarkm	u_int c, ret;
10862053Smarkm	int error = 0;
10962840Smarkm	void *random_buf;
11062053Smarkm
11167286Speter	while (!random_state.seeded) {
11267286Speter		if (flag & IO_NDELAY)
11367286Speter			error =  EWOULDBLOCK;
11467112Smarkm		else
11567286Speter			error = tsleep(&random_state, PUSER|PCATCH, "rndblk", 0);
11667286Speter		if (error != 0)
11767286Speter			return error;
11867112Smarkm	}
11967286Speter	c = min(uio->uio_resid, PAGE_SIZE);
12067286Speter	random_buf = (void *)malloc(c, M_TEMP, M_WAITOK);
12167286Speter	while (uio->uio_resid > 0 && error == 0) {
12267286Speter		ret = read_random_real(random_buf, c);
12367286Speter		error = uiomove(random_buf, ret, uio);
12467286Speter	}
12567286Speter	free(random_buf, M_TEMP);
12662053Smarkm	return error;
12762053Smarkm}
12862053Smarkm
12962053Smarkmstatic int
13062765Smarkmrandom_write(dev_t dev, struct uio *uio, int flag)
13162053Smarkm{
13262053Smarkm	u_int c;
13362053Smarkm	int error = 0;
13462840Smarkm	void *random_buf;
13562053Smarkm
13662765Smarkm	random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
13762053Smarkm	while (uio->uio_resid > 0) {
13862053Smarkm		c = min(uio->uio_resid, PAGE_SIZE);
13962765Smarkm		error = uiomove(random_buf, c, uio);
14062053Smarkm		if (error)
14162053Smarkm			break;
14263306Smarkm		write_random(random_buf, c);
14362053Smarkm	}
14462765Smarkm	free(random_buf, M_TEMP);
14562053Smarkm	return error;
14662053Smarkm}
14762053Smarkm
14862053Smarkmstatic int
14965686Smarkmrandom_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
15065686Smarkm{
15165686Smarkm	return ENOTTY;
15265686Smarkm}
15365686Smarkm
15465686Smarkmstatic int
15567112Smarkmrandom_poll(dev_t dev, int events, struct proc *p)
15667112Smarkm{
15767112Smarkm	int revents;
15867112Smarkm
15967112Smarkm	revents = 0;
16067112Smarkm	if (events & (POLLIN | POLLRDNORM)) {
16167112Smarkm		if (random_state.seeded)
16267112Smarkm			revents = events & (POLLIN | POLLRDNORM);
16367112Smarkm		else
16467112Smarkm			selrecord(p, &random_state.rsel);
16567112Smarkm	}
16667112Smarkm	return revents;
16767112Smarkm}
16867112Smarkm
16967112Smarkmstatic int
17062053Smarkmrandom_modevent(module_t mod, int type, void *data)
17162053Smarkm{
17271037Smarkm	struct sysctl_oid *node_base, *node1, *node2;
17371037Smarkm	int error, i;
17465686Smarkm
17562053Smarkm	switch(type) {
17662053Smarkm	case MOD_LOAD:
17765686Smarkm		error = random_init();
17865686Smarkm		if (error != 0)
17965686Smarkm			return error;
18071037Smarkm
18171037Smarkm		random_sysctl[sysctlcount++] = node_base =
18271037Smarkm			SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_kern),
18371037Smarkm				OID_AUTO, "random", CTLFLAG_RW, 0,
18471037Smarkm				"Random Number Generator");
18571037Smarkm		random_sysctl[sysctlcount++] = node1 =
18671037Smarkm			SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(node_base),
18771037Smarkm				OID_AUTO, "sys", CTLFLAG_RW, 0,
18871037Smarkm				"Entropy Device Parameters");
18971037Smarkm		random_sysctl[sysctlcount++] =
19071037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node1),
19171037Smarkm				OID_AUTO, "seeded", CTLFLAG_RW,
19271037Smarkm				&random_state.seeded, 0, "Seeded State");
19371037Smarkm		random_sysctl[sysctlcount++] = node2 =
19471037Smarkm			SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(node_base),
19571037Smarkm				OID_AUTO, "yarrow", CTLFLAG_RW, 0,
19671037Smarkm				"Yarrow Parameters");
19771037Smarkm		random_sysctl[sysctlcount++] =
19871037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
19971037Smarkm				OID_AUTO, "gengateinterval", CTLFLAG_RW,
20071037Smarkm				&random_state.gengateinterval, 0,
20171037Smarkm				"Generator Gate Interval");
20271037Smarkm		random_sysctl[sysctlcount++] =
20371037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
20471037Smarkm				OID_AUTO, "bins", CTLFLAG_RW,
20571037Smarkm				&random_state.bins, 0,
20671037Smarkm				"Execution time tuner");
20771037Smarkm		random_sysctl[sysctlcount++] =
20871037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
20971037Smarkm				OID_AUTO, "fastthresh", CTLFLAG_RW,
21071037Smarkm				&random_state.pool[0].thresh, 0,
21171037Smarkm				"Fast pool reseed threshhold");
21271037Smarkm		random_sysctl[sysctlcount++] =
21371037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
21471037Smarkm				OID_AUTO, "slowthresh", CTLFLAG_RW,
21571037Smarkm				&random_state.pool[1].thresh, 0,
21671037Smarkm				"Slow pool reseed threshhold");
21771037Smarkm		random_sysctl[sysctlcount++] =
21871037Smarkm			SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
21971037Smarkm				OID_AUTO, "slowoverthresh", CTLFLAG_RW,
22071037Smarkm				&random_state.slowoverthresh, 0,
22171037Smarkm				"Slow pool over-threshhold reseed");
22271037Smarkm
22362053Smarkm		if (bootverbose)
22462053Smarkm			printf("random: <entropy source>\n");
22562765Smarkm		random_dev = make_dev(&random_cdevsw, RANDOM_MINOR, UID_ROOT,
22662149Smarkm			GID_WHEEL, 0666, "random");
22762765Smarkm		urandom_dev = make_dev(&random_cdevsw, URANDOM_MINOR, UID_ROOT,
22865686Smarkm			GID_WHEEL, 0666, "urandom"); /* XXX Temporary */
22962053Smarkm		return 0;
23062053Smarkm
23162053Smarkm	case MOD_UNLOAD:
23262765Smarkm		random_deinit();
23362765Smarkm		destroy_dev(random_dev);
23465686Smarkm		destroy_dev(urandom_dev); /* XXX Temporary */
23571037Smarkm		for (i = sysctlcount - 1; i >= 0; i--)
23671037Smarkm			if (sysctl_remove_oid(random_sysctl[i], 1, 0) == EINVAL)
23771037Smarkm				panic("random: removing sysctl");
23862053Smarkm		return 0;
23962053Smarkm
24062053Smarkm	case MOD_SHUTDOWN:
24162053Smarkm		return 0;
24262053Smarkm
24362053Smarkm	default:
24462053Smarkm		return EOPNOTSUPP;
24562053Smarkm	}
24662053Smarkm}
24762053Smarkm
24862053SmarkmDEV_MODULE(random, random_modevent, NULL);
249