14db9ae91SMark Murray /*- 2c9ec235cSMark Murray * Copyright (c) 2000 Mark R V Murray 34db9ae91SMark Murray * All rights reserved. 44db9ae91SMark Murray * 54db9ae91SMark Murray * Redistribution and use in source and binary forms, with or without 64db9ae91SMark Murray * modification, are permitted provided that the following conditions 74db9ae91SMark Murray * are met: 84db9ae91SMark Murray * 1. Redistributions of source code must retain the above copyright 94db9ae91SMark Murray * notice, this list of conditions and the following disclaimer 104db9ae91SMark Murray * in this position and unchanged. 114db9ae91SMark Murray * 2. Redistributions in binary form must reproduce the above copyright 124db9ae91SMark Murray * notice, this list of conditions and the following disclaimer in the 134db9ae91SMark Murray * documentation and/or other materials provided with the distribution. 144db9ae91SMark Murray * 154db9ae91SMark Murray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 164db9ae91SMark Murray * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 174db9ae91SMark Murray * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 184db9ae91SMark Murray * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 194db9ae91SMark Murray * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 204db9ae91SMark Murray * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 214db9ae91SMark Murray * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 224db9ae91SMark Murray * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 234db9ae91SMark Murray * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 244db9ae91SMark Murray * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 254db9ae91SMark Murray * 264db9ae91SMark Murray * $FreeBSD$ 274db9ae91SMark Murray */ 284db9ae91SMark Murray 294db9ae91SMark Murray #include <sys/param.h> 304db9ae91SMark Murray #include <sys/systm.h> 31fb919e4dSMark Murray #include <sys/bus.h> 324db9ae91SMark Murray #include <sys/conf.h> 334db9ae91SMark Murray #include <sys/fcntl.h> 34fb919e4dSMark Murray #include <sys/filio.h> 354db9ae91SMark Murray #include <sys/kernel.h> 3602c986abSMark Murray #include <sys/kthread.h> 37fb919e4dSMark Murray #include <sys/lock.h> 384db9ae91SMark Murray #include <sys/malloc.h> 394db9ae91SMark Murray #include <sys/module.h> 40fb919e4dSMark Murray #include <sys/mutex.h> 41a6278a2aSMark Murray #include <sys/poll.h> 42fb919e4dSMark Murray #include <sys/queue.h> 434db9ae91SMark Murray #include <sys/random.h> 44fb919e4dSMark Murray #include <sys/selinfo.h> 45b79ad7e6SMark Murray #include <sys/sysctl.h> 46fb919e4dSMark Murray #include <sys/uio.h> 4702c986abSMark Murray #include <sys/unistd.h> 48fb919e4dSMark Murray #include <sys/vnode.h> 494db9ae91SMark Murray 5002c986abSMark Murray #include <machine/bus.h> 5102c986abSMark Murray #include <machine/cpu.h> 5202c986abSMark Murray #include <machine/resource.h> 5302c986abSMark Murray 5402c986abSMark Murray #include <dev/random/randomdev.h> 554db9ae91SMark Murray 567aa4389aSMark Murray static d_open_t random_open; 5750636159SMark Murray static d_close_t random_close; 58c9ec235cSMark Murray static d_read_t random_read; 59c9ec235cSMark Murray static d_write_t random_write; 604d87a031SMark Murray static d_ioctl_t random_ioctl; 61a6278a2aSMark Murray static d_poll_t random_poll; 624db9ae91SMark Murray 634db9ae91SMark Murray #define CDEV_MAJOR 2 644db9ae91SMark Murray #define RANDOM_MINOR 3 654db9ae91SMark Murray 664db9ae91SMark Murray static struct cdevsw random_cdevsw = { 677aa4389aSMark Murray /* open */ random_open, 6850636159SMark Murray /* close */ random_close, 69c9ec235cSMark Murray /* read */ random_read, 70c9ec235cSMark Murray /* write */ random_write, 714d87a031SMark Murray /* ioctl */ random_ioctl, 72a6278a2aSMark Murray /* poll */ random_poll, 734db9ae91SMark Murray /* mmap */ nommap, 744db9ae91SMark Murray /* strategy */ nostrategy, 754db9ae91SMark Murray /* name */ "random", 764db9ae91SMark Murray /* maj */ CDEV_MAJOR, 774db9ae91SMark Murray /* dump */ nodump, 784db9ae91SMark Murray /* psize */ nopsize, 794db9ae91SMark Murray /* flags */ 0, 804db9ae91SMark Murray }; 814db9ae91SMark Murray 8202c986abSMark Murray static void random_kthread(void *); 8302c986abSMark Murray static void random_harvest_internal(u_int64_t, void *, u_int, u_int, u_int, enum esource); 8402c986abSMark Murray static void random_write_internal(void *, u_int); 8502c986abSMark Murray 8602c986abSMark Murray /* Ring buffer holding harvested entropy */ 8702c986abSMark Murray static struct harvestring { 8802c986abSMark Murray volatile u_int head; 8902c986abSMark Murray volatile u_int tail; 9002c986abSMark Murray struct harvest data[HARVEST_RING_SIZE]; 9102c986abSMark Murray } harvestring; 9202c986abSMark Murray 9302c986abSMark Murray static struct random_systat { 9402c986abSMark Murray u_int seeded; /* 0 causes blocking 1 allows normal output */ 9502c986abSMark Murray u_int burst; /* number of events to do before sleeping */ 9602c986abSMark Murray struct selinfo rsel; /* For poll(2) */ 9702c986abSMark Murray } random_systat; 9802c986abSMark Murray 9902c986abSMark Murray /* <0 to end the kthread, 0 to let it run */ 10002c986abSMark Murray static int random_kthread_control = 0; 10102c986abSMark Murray 10202c986abSMark Murray static struct proc *random_kthread_proc; 10302c986abSMark Murray 1044db9ae91SMark Murray /* For use with make_dev(9)/destroy_dev(9). */ 105c9ec235cSMark Murray static dev_t random_dev; 10602c986abSMark Murray static dev_t urandom_dev; 1074db9ae91SMark Murray 10802c986abSMark Murray static int 10902c986abSMark Murray random_check_boolean(SYSCTL_HANDLER_ARGS) 11002c986abSMark Murray { 11102c986abSMark Murray if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0) 11202c986abSMark Murray *(u_int *)(oidp->oid_arg1) = 1; 11302c986abSMark Murray return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); 11402c986abSMark Murray } 11502c986abSMark Murray 11602c986abSMark Murray RANDOM_CHECK_UINT(burst, 0, 20); 11702c986abSMark Murray 11802c986abSMark Murray SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 11902c986abSMark Murray 0, "Random Number Generator"); 12002c986abSMark Murray SYSCTL_NODE(_kern_random, OID_AUTO, sys, CTLFLAG_RW, 12102c986abSMark Murray 0, "Entropy Device Parameters"); 12202c986abSMark Murray SYSCTL_PROC(_kern_random_sys, OID_AUTO, seeded, 12302c986abSMark Murray CTLTYPE_INT|CTLFLAG_RW, &random_systat.seeded, 1, 12402c986abSMark Murray random_check_boolean, "I", "Seeded State"); 12502c986abSMark Murray SYSCTL_PROC(_kern_random_sys, OID_AUTO, burst, 12602c986abSMark Murray CTLTYPE_INT|CTLFLAG_RW, &random_systat.burst, 20, 12702c986abSMark Murray random_check_uint_burst, "I", "Harvest Burst Size"); 12802c986abSMark Murray SYSCTL_NODE(_kern_random_sys, OID_AUTO, harvest, CTLFLAG_RW, 12902c986abSMark Murray 0, "Entropy Sources"); 13002c986abSMark Murray SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, ethernet, 13102c986abSMark Murray CTLTYPE_INT|CTLFLAG_RW, &harvest.ethernet, 0, 13202c986abSMark Murray random_check_boolean, "I", "Harvest NIC entropy"); 13302c986abSMark Murray SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, point_to_point, 13402c986abSMark Murray CTLTYPE_INT|CTLFLAG_RW, &harvest.point_to_point, 0, 13502c986abSMark Murray random_check_boolean, "I", "Harvest serial net entropy"); 13602c986abSMark Murray SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, interrupt, 13702c986abSMark Murray CTLTYPE_INT|CTLFLAG_RW, &harvest.interrupt, 0, 13802c986abSMark Murray random_check_boolean, "I", "Harvest IRQ entropy"); 1394db9ae91SMark Murray 1404db9ae91SMark Murray static int 1417aa4389aSMark Murray random_open(dev_t dev, int flags, int fmt, struct proc *p) 1427aa4389aSMark Murray { 1437aa4389aSMark Murray if ((flags & FWRITE) && (securelevel > 0 || suser(p))) 1447aa4389aSMark Murray return EPERM; 1457aa4389aSMark Murray else 1467aa4389aSMark Murray return 0; 1477aa4389aSMark Murray } 1487aa4389aSMark Murray 1497aa4389aSMark Murray static int 15050636159SMark Murray random_close(dev_t dev, int flags, int fmt, struct proc *p) 15150636159SMark Murray { 1525d4e386eSMark Murray if ((flags & FWRITE) && !(securelevel > 0 || suser(p))) 15350636159SMark Murray random_reseed(); 15450636159SMark Murray return 0; 15550636159SMark Murray } 15650636159SMark Murray 15750636159SMark Murray static int 158c9ec235cSMark Murray random_read(dev_t dev, struct uio *uio, int flag) 1594db9ae91SMark Murray { 1604db9ae91SMark Murray u_int c, ret; 1614db9ae91SMark Murray int error = 0; 162769afb04SMark Murray void *random_buf; 1634db9ae91SMark Murray 16402c986abSMark Murray while (!random_systat.seeded) { 16593d88e00SPeter Wemm if (flag & IO_NDELAY) 166a6278a2aSMark Murray error = EWOULDBLOCK; 16793d88e00SPeter Wemm else 16802c986abSMark Murray error = tsleep(&random_systat, PUSER|PCATCH, 16902c986abSMark Murray "block", 0); 17093d88e00SPeter Wemm if (error != 0) 17193d88e00SPeter Wemm return error; 172a6278a2aSMark Murray } 1734db9ae91SMark Murray c = min(uio->uio_resid, PAGE_SIZE); 174c9ec235cSMark Murray random_buf = (void *)malloc(c, M_TEMP, M_WAITOK); 1754db9ae91SMark Murray while (uio->uio_resid > 0 && error == 0) { 176a6278a2aSMark Murray ret = read_random_real(random_buf, c); 177c9ec235cSMark Murray error = uiomove(random_buf, ret, uio); 1784db9ae91SMark Murray } 179c9ec235cSMark Murray free(random_buf, M_TEMP); 1804db9ae91SMark Murray return error; 1814db9ae91SMark Murray } 1824db9ae91SMark Murray 1834db9ae91SMark Murray static int 184c9ec235cSMark Murray random_write(dev_t dev, struct uio *uio, int flag) 1854db9ae91SMark Murray { 1864db9ae91SMark Murray u_int c; 18702c986abSMark Murray int error; 188769afb04SMark Murray void *random_buf; 1894db9ae91SMark Murray 19002c986abSMark Murray error = 0; 191c9ec235cSMark Murray random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); 1924db9ae91SMark Murray while (uio->uio_resid > 0) { 1934db9ae91SMark Murray c = min(uio->uio_resid, PAGE_SIZE); 194c9ec235cSMark Murray error = uiomove(random_buf, c, uio); 1954db9ae91SMark Murray if (error) 1964db9ae91SMark Murray break; 19702c986abSMark Murray random_write_internal(random_buf, c); 1984db9ae91SMark Murray } 199c9ec235cSMark Murray free(random_buf, M_TEMP); 2004db9ae91SMark Murray return error; 2014db9ae91SMark Murray } 2024db9ae91SMark Murray 2034db9ae91SMark Murray static int 2044d87a031SMark Murray random_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 2054d87a031SMark Murray { 20641bc9751SMark Murray switch (cmd) { 20741bc9751SMark Murray /* Really handled in upper layer */ 20841bc9751SMark Murray case FIOASYNC: 20941bc9751SMark Murray case FIONBIO: 21041bc9751SMark Murray return 0; 21141bc9751SMark Murray default: 2124d87a031SMark Murray return ENOTTY; 2134d87a031SMark Murray } 21441bc9751SMark Murray } 2154d87a031SMark Murray 2164d87a031SMark Murray static int 217a6278a2aSMark Murray random_poll(dev_t dev, int events, struct proc *p) 218a6278a2aSMark Murray { 219a6278a2aSMark Murray int revents; 220a6278a2aSMark Murray 221a6278a2aSMark Murray revents = 0; 222a6278a2aSMark Murray if (events & (POLLIN | POLLRDNORM)) { 22302c986abSMark Murray if (random_systat.seeded) 224a6278a2aSMark Murray revents = events & (POLLIN | POLLRDNORM); 225a6278a2aSMark Murray else 22602c986abSMark Murray selrecord(p, &random_systat.rsel); 227a6278a2aSMark Murray } 228a6278a2aSMark Murray return revents; 229a6278a2aSMark Murray } 230a6278a2aSMark Murray 231a6278a2aSMark Murray static int 2324db9ae91SMark Murray random_modevent(module_t mod, int type, void *data) 2334db9ae91SMark Murray { 23402c986abSMark Murray int error; 2354d87a031SMark Murray 2364db9ae91SMark Murray switch(type) { 2374db9ae91SMark Murray case MOD_LOAD: 23802c986abSMark Murray random_init(); 239b79ad7e6SMark Murray 24002c986abSMark Murray /* This can be turned off by the very paranoid 24102c986abSMark Murray * a reseed will turn it back on. 24202c986abSMark Murray */ 24302c986abSMark Murray random_systat.seeded = 1; 24402c986abSMark Murray 24502c986abSMark Murray /* Number of envents to process off the harvest 24602c986abSMark Murray * queue before giving it a break and sleeping 24702c986abSMark Murray */ 24802c986abSMark Murray random_systat.burst = 20; 24902c986abSMark Murray 25002c986abSMark Murray /* Initialise the harvest ringbuffer */ 25102c986abSMark Murray harvestring.head = 0; 25202c986abSMark Murray harvestring.tail = 0; 253b79ad7e6SMark Murray 2544db9ae91SMark Murray if (bootverbose) 2554db9ae91SMark Murray printf("random: <entropy source>\n"); 256c9ec235cSMark Murray random_dev = make_dev(&random_cdevsw, RANDOM_MINOR, UID_ROOT, 2571f67cd87SMark Murray GID_WHEEL, 0666, "random"); 25802c986abSMark Murray urandom_dev = make_dev_alias(random_dev, "urandom"); 25902c986abSMark Murray 26002c986abSMark Murray /* Start the hash/reseed thread */ 26102c986abSMark Murray error = kthread_create(random_kthread, NULL, 26202c986abSMark Murray &random_kthread_proc, RFHIGHPID, "random"); 26302c986abSMark Murray if (error != 0) 26402c986abSMark Murray return error; 26502c986abSMark Murray 26602c986abSMark Murray /* Register the randomness harvesting routine */ 26702c986abSMark Murray random_init_harvester(random_harvest_internal, 26802c986abSMark Murray read_random_real); 26902c986abSMark Murray 2704db9ae91SMark Murray return 0; 2714db9ae91SMark Murray 2724db9ae91SMark Murray case MOD_UNLOAD: 27302c986abSMark Murray /* Deregister the randomness harvesting routine */ 27402c986abSMark Murray random_deinit_harvester(); 27502c986abSMark Murray 27602c986abSMark Murray /* Command the hash/reseed thread to end and 27702c986abSMark Murray * wait for it to finish 27802c986abSMark Murray */ 27902c986abSMark Murray random_kthread_control = -1; 28002c986abSMark Murray tsleep((void *)&random_kthread_control, PUSER, "term", 0); 28102c986abSMark Murray 282c9ec235cSMark Murray random_deinit(); 28302c986abSMark Murray 284c9ec235cSMark Murray destroy_dev(random_dev); 28502c986abSMark Murray destroy_dev(urandom_dev); 2864db9ae91SMark Murray return 0; 2874db9ae91SMark Murray 2884db9ae91SMark Murray case MOD_SHUTDOWN: 2894db9ae91SMark Murray return 0; 2904db9ae91SMark Murray 2914db9ae91SMark Murray default: 2924db9ae91SMark Murray return EOPNOTSUPP; 2934db9ae91SMark Murray } 2944db9ae91SMark Murray } 2954db9ae91SMark Murray 2964db9ae91SMark Murray DEV_MODULE(random, random_modevent, NULL); 29702c986abSMark Murray 29802c986abSMark Murray static void 29902c986abSMark Murray random_kthread(void *arg /* NOTUSED */) 30002c986abSMark Murray { 30102c986abSMark Murray struct harvest *event; 30202c986abSMark Murray int newtail, burst; 30302c986abSMark Murray 30402c986abSMark Murray /* Drain the harvest queue (in 'burst' size chunks, 30502c986abSMark Murray * if 'burst' > 0. If 'burst' == 0, then completely 30602c986abSMark Murray * drain the queue. 30702c986abSMark Murray */ 30802c986abSMark Murray for (burst = 0; ; burst++) { 30902c986abSMark Murray 31002c986abSMark Murray if ((harvestring.tail == harvestring.head) || 31102c986abSMark Murray (random_systat.burst && burst == random_systat.burst)) { 31202c986abSMark Murray tsleep(&harvestring, PUSER, "sleep", hz/10); 31302c986abSMark Murray burst = 0; 31402c986abSMark Murray 31502c986abSMark Murray } 31602c986abSMark Murray else { 31702c986abSMark Murray 31802c986abSMark Murray /* Suck a harvested entropy event out of the queue and 31902c986abSMark Murray * hand it to the event processor 32002c986abSMark Murray */ 32102c986abSMark Murray 32202c986abSMark Murray newtail = (harvestring.tail + 1) & HARVEST_RING_MASK; 32302c986abSMark Murray event = &harvestring.data[harvestring.tail]; 32402c986abSMark Murray 32502c986abSMark Murray /* Bump the ring counter. This action is assumed 32602c986abSMark Murray * to be atomic. 32702c986abSMark Murray */ 32802c986abSMark Murray harvestring.tail = newtail; 32902c986abSMark Murray 33002c986abSMark Murray random_process_event(event); 33102c986abSMark Murray 33202c986abSMark Murray } 33302c986abSMark Murray 33402c986abSMark Murray /* Is the thread scheduled for a shutdown? */ 33502c986abSMark Murray if (random_kthread_control != 0) { 33602c986abSMark Murray #ifdef DEBUG 33702c986abSMark Murray mtx_lock(&Giant); 33802c986abSMark Murray printf("Random kthread setting terminate\n"); 33902c986abSMark Murray mtx_unlock(&Giant); 34002c986abSMark Murray #endif 34102c986abSMark Murray random_set_wakeup_exit(&random_kthread_control); 34202c986abSMark Murray /* NOTREACHED */ 34302c986abSMark Murray break; 34402c986abSMark Murray } 34502c986abSMark Murray 34602c986abSMark Murray } 34702c986abSMark Murray 34802c986abSMark Murray } 34902c986abSMark Murray 35002c986abSMark Murray /* Entropy harvesting routine. This is supposed to be fast; do 35102c986abSMark Murray * not do anything slow in here! 35202c986abSMark Murray */ 35302c986abSMark Murray static void 35402c986abSMark Murray random_harvest_internal(u_int64_t somecounter, void *entropy, u_int count, 35502c986abSMark Murray u_int bits, u_int frac, enum esource origin) 35602c986abSMark Murray { 35702c986abSMark Murray struct harvest *harvest; 35802c986abSMark Murray int newhead; 35902c986abSMark Murray 36002c986abSMark Murray newhead = (harvestring.head + 1) & HARVEST_RING_MASK; 36102c986abSMark Murray 36202c986abSMark Murray if (newhead != harvestring.tail) { 36302c986abSMark Murray 36402c986abSMark Murray /* Add the harvested data to the ring buffer */ 36502c986abSMark Murray 36602c986abSMark Murray harvest = &harvestring.data[harvestring.head]; 36702c986abSMark Murray 36802c986abSMark Murray /* Stuff the harvested data into the ring */ 36902c986abSMark Murray harvest->somecounter = somecounter; 37002c986abSMark Murray count = count > HARVESTSIZE ? HARVESTSIZE : count; 37102c986abSMark Murray memcpy(harvest->entropy, entropy, count); 37202c986abSMark Murray harvest->size = count; 37302c986abSMark Murray harvest->bits = bits; 37402c986abSMark Murray harvest->frac = frac; 37502c986abSMark Murray harvest->source = origin < ENTROPYSOURCE ? origin : 0; 37602c986abSMark Murray 37702c986abSMark Murray /* Bump the ring counter. This action is assumed 37802c986abSMark Murray * to be atomic. 37902c986abSMark Murray */ 38002c986abSMark Murray harvestring.head = newhead; 38102c986abSMark Murray 38202c986abSMark Murray } 38302c986abSMark Murray 38402c986abSMark Murray } 38502c986abSMark Murray 38602c986abSMark Murray static void 38702c986abSMark Murray random_write_internal(void *buf, u_int count) 38802c986abSMark Murray { 38902c986abSMark Murray u_int i; 39002c986abSMark Murray 39102c986abSMark Murray /* Break the input up into HARVESTSIZE chunks. 39202c986abSMark Murray * The writer has too much control here, so "estimate" the 39302c986abSMark Murray * the entropy as zero. 39402c986abSMark Murray */ 39502c986abSMark Murray for (i = 0; i < count; i += HARVESTSIZE) { 39602c986abSMark Murray random_harvest_internal(get_cyclecount(), (char *)buf + i, 39702c986abSMark Murray HARVESTSIZE, 0, 0, RANDOM_WRITE); 39802c986abSMark Murray } 39902c986abSMark Murray 40002c986abSMark Murray /* Maybe the loop iterated at least once */ 40102c986abSMark Murray if (i > count) 40202c986abSMark Murray i -= HARVESTSIZE; 40302c986abSMark Murray 40402c986abSMark Murray /* Get the last bytes even if the input length is not 40502c986abSMark Murray * a multiple of HARVESTSIZE. 40602c986abSMark Murray */ 40702c986abSMark Murray count %= HARVESTSIZE; 40802c986abSMark Murray if (count) { 40902c986abSMark Murray random_harvest_internal(get_cyclecount(), (char *)buf + i, 41002c986abSMark Murray count, 0, 0, RANDOM_WRITE); 41102c986abSMark Murray } 41202c986abSMark Murray } 41302c986abSMark Murray 41402c986abSMark Murray void 41502c986abSMark Murray random_unblock(void) 41602c986abSMark Murray { 41702c986abSMark Murray if (!random_systat.seeded) { 41802c986abSMark Murray random_systat.seeded = 1; 41902c986abSMark Murray selwakeup(&random_systat.rsel); 42002c986abSMark Murray wakeup(&random_systat); 42102c986abSMark Murray } 42202c986abSMark Murray } 423