Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: random.c,v 1.12 2024/08/27 00:56:47 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2019 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Taylor R. Campbell.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * /dev/random, /dev/urandom -- stateless version
     34  *
     35  *	For short reads from /dev/urandom, up to 256 bytes, read from a
     36  *	per-CPU NIST Hash_DRBG instance that is reseeded as soon as the
     37  *	system has enough entropy.
     38  *
     39  *	For all other reads, instantiate a fresh NIST Hash_DRBG from
     40  *	the global entropy pool, and draw from it.
     41  *
     42  *	Each read is independent; there is no per-open state.
     43  *	Concurrent reads from the same open run in parallel.
     44  *
     45  *	Reading from /dev/random may block until entropy is available.
     46  *	Either device may return short reads if interrupted.
     47  */
     48 
     49 #include <sys/cdefs.h>
     50 __KERNEL_RCSID(0, "$NetBSD: random.c,v 1.12 2024/08/27 00:56:47 riastradh Exp $");
     51 
     52 #include <sys/param.h>
     53 #include <sys/types.h>
     54 #include <sys/atomic.h>
     55 #include <sys/conf.h>
     56 #include <sys/cprng.h>
     57 #include <sys/entropy.h>
     58 #include <sys/errno.h>
     59 #include <sys/event.h>
     60 #include <sys/fcntl.h>
     61 #include <sys/kauth.h>
     62 #include <sys/kmem.h>
     63 #include <sys/lwp.h>
     64 #include <sys/poll.h>
     65 #include <sys/random.h>
     66 #include <sys/rnd.h>
     67 #include <sys/rndsource.h>
     68 #include <sys/signalvar.h>
     69 #include <sys/systm.h>
     70 #include <sys/vnode.h>		/* IO_NDELAY */
     71 
     72 #include "ioconf.h"
     73 
     74 static dev_type_open(random_open);
     75 static dev_type_close(random_close);
     76 static dev_type_ioctl(random_ioctl);
     77 static dev_type_poll(random_poll);
     78 static dev_type_kqfilter(random_kqfilter);
     79 static dev_type_read(random_read);
     80 static dev_type_write(random_write);
     81 
     82 const struct cdevsw rnd_cdevsw = {
     83 	.d_open = random_open,
     84 	.d_close = random_close,
     85 	.d_read = random_read,
     86 	.d_write = random_write,
     87 	.d_ioctl = random_ioctl,
     88 	.d_stop = nostop,
     89 	.d_tty = notty,
     90 	.d_poll = random_poll,
     91 	.d_mmap = nommap,
     92 	.d_kqfilter = random_kqfilter,
     93 	.d_discard = nodiscard,
     94 	.d_flag = D_OTHER|D_MPSAFE,
     95 };
     96 
     97 #define	RANDOM_BUFSIZE	512	/* XXX pulled from arse */
     98 
     99 /* Entropy source for writes to /dev/random and /dev/urandom */
    100 static krndsource_t	user_rndsource;
    101 
    102 void
    103 rndattach(int num)
    104 {
    105 
    106 	rnd_attach_source(&user_rndsource, "/dev/random", RND_TYPE_UNKNOWN,
    107 	    RND_FLAG_COLLECT_VALUE);
    108 }
    109 
    110 static int
    111 random_open(dev_t dev, int flags, int fmt, struct lwp *l)
    112 {
    113 
    114 	/* Validate minor.  */
    115 	switch (minor(dev)) {
    116 	case RND_DEV_RANDOM:
    117 	case RND_DEV_URANDOM:
    118 		break;
    119 	default:
    120 		return ENXIO;
    121 	}
    122 
    123 	return 0;
    124 }
    125 
    126 static int
    127 random_close(dev_t dev, int flags, int fmt, struct lwp *l)
    128 {
    129 
    130 	/* Success!  */
    131 	return 0;
    132 }
    133 
    134 static int
    135 random_ioctl(dev_t dev, unsigned long cmd, void *data, int flag, struct lwp *l)
    136 {
    137 
    138 	/*
    139 	 * No non-blocking/async options; otherwise defer to
    140 	 * entropy_ioctl.
    141 	 */
    142 	switch (cmd) {
    143 	case FIONBIO:
    144 	case FIOASYNC:
    145 		return 0;
    146 	default:
    147 		return entropy_ioctl(cmd, data);
    148 	}
    149 }
    150 
    151 static int
    152 random_poll(dev_t dev, int events, struct lwp *l)
    153 {
    154 
    155 	/* /dev/random may block; /dev/urandom is always ready.  */
    156 	switch (minor(dev)) {
    157 	case RND_DEV_RANDOM:
    158 		return entropy_poll(events);
    159 	case RND_DEV_URANDOM:
    160 		return events & (POLLIN|POLLRDNORM | POLLOUT|POLLWRNORM);
    161 	default:
    162 		return 0;
    163 	}
    164 }
    165 
    166 static int
    167 random_kqfilter(dev_t dev, struct knote *kn)
    168 {
    169 
    170 	/* Validate the event filter.  */
    171 	switch (kn->kn_filter) {
    172 	case EVFILT_READ:
    173 	case EVFILT_WRITE:
    174 		break;
    175 	default:
    176 		return EINVAL;
    177 	}
    178 
    179 	/* /dev/random may block; /dev/urandom never does.  */
    180 	switch (minor(dev)) {
    181 	case RND_DEV_RANDOM:
    182 		if (kn->kn_filter == EVFILT_READ)
    183 			return entropy_kqfilter(kn);
    184 		/* FALLTHROUGH */
    185 	case RND_DEV_URANDOM:
    186 		kn->kn_fop = &seltrue_filtops;
    187 		return 0;
    188 	default:
    189 		return ENXIO;
    190 	}
    191 }
    192 
    193 /*
    194  * random_read(dev, uio, flags)
    195  *
    196  *	Generate data from a PRNG seeded from the entropy pool.
    197  *
    198  *	- If /dev/random, block until we have full entropy, or fail
    199  *	  with EWOULDBLOCK, and if `depleting' entropy, return at most
    200  *	  the entropy pool's capacity at once.
    201  *
    202  *	- If /dev/urandom, generate data from whatever is in the
    203  *	  entropy pool now.
    204  *
    205  *	On interrupt, return a short read, but not shorter than 256
    206  *	bytes (actually, no shorter than RANDOM_BUFSIZE bytes, which is
    207  *	512 for hysterical raisins).
    208  */
    209 static int
    210 random_read(dev_t dev, struct uio *uio, int flags)
    211 {
    212 	int gflags;
    213 
    214 	/* Set the appropriate GRND_* mode.  */
    215 	switch (minor(dev)) {
    216 	case RND_DEV_RANDOM:
    217 		gflags = GRND_RANDOM;
    218 		break;
    219 	case RND_DEV_URANDOM:
    220 		gflags = GRND_INSECURE;
    221 		break;
    222 	default:
    223 		return ENXIO;
    224 	}
    225 
    226 	/*
    227 	 * Set GRND_NONBLOCK if the user requested IO_NDELAY (i.e., the
    228 	 * file was opened with O_NONBLOCK).
    229 	 */
    230 	if (flags & IO_NDELAY)
    231 		gflags |= GRND_NONBLOCK;
    232 
    233 	/* Defer to getrandom.  */
    234 	return dogetrandom(uio, gflags);
    235 }
    236 
    237 /*
    238  * random_write(dev, uio, flags)
    239  *
    240  *	Enter data from uio into the entropy pool.
    241  *
    242  *	Assume privileged users provide full entropy, and unprivileged
    243  *	users provide no entropy.  If you have a nonuniform source of
    244  *	data with n bytes of min-entropy, hash it with an XOF like
    245  *	SHAKE128 into exactly n bytes first.
    246  */
    247 static int
    248 random_write(dev_t dev, struct uio *uio, int flags)
    249 {
    250 	kauth_cred_t cred = kauth_cred_get();
    251 	uint8_t *buf;
    252 	bool privileged = false, any = false;
    253 	int error = 0;
    254 
    255 	/* Verify user's authorization to affect the entropy pool.  */
    256 	error = kauth_authorize_device(cred, KAUTH_DEVICE_RND_ADDDATA,
    257 	    NULL, NULL, NULL, NULL);
    258 	if (error)
    259 		return error;
    260 
    261 	/*
    262 	 * Check whether user is privileged.  If so, assume user
    263 	 * furnishes full-entropy data; if not, accept user's data but
    264 	 * assume it has zero entropy when we do accounting.  If you
    265 	 * want to specify less entropy, use ioctl(RNDADDDATA).
    266 	 */
    267 	if (kauth_authorize_device(cred, KAUTH_DEVICE_RND_ADDDATA_ESTIMATE,
    268 		NULL, NULL, NULL, NULL) == 0)
    269 		privileged = true;
    270 
    271 	/* Get a buffer for transfers.  */
    272 	buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
    273 
    274 	/* Consume data.  */
    275 	while (uio->uio_resid) {
    276 		size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
    277 
    278 		/* Transfer n bytes in and enter them into the pool.  */
    279 		error = uiomove(buf, n, uio);
    280 		if (error)
    281 			break;
    282 		rnd_add_data(&user_rndsource, buf, n, privileged ? n*NBBY : 0);
    283 		any = true;
    284 
    285 		/* Now's a good time to yield if needed.  */
    286 		preempt_point();
    287 
    288 		/* Check for interruption.  */
    289 		if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
    290 		    sigispending(curlwp, 0)) {
    291 			error = EINTR;
    292 			break;
    293 		}
    294 	}
    295 
    296 	/* Zero the buffer and free it.  */
    297 	explicit_memset(buf, 0, RANDOM_BUFSIZE);
    298 	kmem_free(buf, RANDOM_BUFSIZE);
    299 
    300 	/* If we added anything, consolidate entropy now.  */
    301 	if (any && error == 0)
    302 		error = entropy_consolidate();
    303 
    304 	return error;
    305 }
    306