1 /* $NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2020 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 * getrandom() system call
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: sys_getrandom.c,v 1.2 2021/12/28 13:22:43 riastradh Exp $");
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41
42 #include <sys/atomic.h>
43 #include <sys/cprng.h>
44 #include <sys/entropy.h>
45 #include <sys/kmem.h>
46 #include <sys/lwp.h>
47 #include <sys/proc.h>
48 #include <sys/random.h>
49 #include <sys/sched.h>
50 #include <sys/signalvar.h>
51 #include <sys/syscallargs.h>
52 #include <sys/uio.h>
53
54 #include <crypto/nist_hash_drbg/nist_hash_drbg.h>
55
56 #define RANDOM_BUFSIZE 512
57
58 int
59 dogetrandom(struct uio *uio, unsigned int flags)
60 {
61 uint8_t seed[NIST_HASH_DRBG_SEEDLEN_BYTES] = {0};
62 struct nist_hash_drbg drbg;
63 uint8_t *buf;
64 int extractflags = 0;
65 int error;
66
67 KASSERT((flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) == 0);
68 KASSERT((flags & (GRND_RANDOM|GRND_INSECURE)) !=
69 (GRND_RANDOM|GRND_INSECURE));
70
71 /* Get a buffer for transfers. */
72 buf = kmem_alloc(RANDOM_BUFSIZE, KM_SLEEP);
73
74 /*
75 * Fast path: for short reads other than from /dev/random, if
76 * seeded or if INSECURE, just draw from per-CPU cprng_strong.
77 */
78 if (uio->uio_resid <= RANDOM_BUFSIZE &&
79 !ISSET(flags, GRND_RANDOM) &&
80 (entropy_ready() || ISSET(flags, GRND_INSECURE))) {
81 /* Generate data and transfer it out. */
82 cprng_strong(user_cprng, buf, uio->uio_resid, 0);
83 error = uiomove(buf, uio->uio_resid, uio);
84 goto out;
85 }
86
87 /*
88 * Try to get a seed from the entropy pool. Fail if we would
89 * block. If GRND_INSECURE, always return something even if it
90 * is partial entropy; if !GRND_INSECURE, set ENTROPY_HARDFAIL
91 * in order to tell entropy_extract not to bother drawing
92 * anything from a partial pool if we can't get full entropy.
93 */
94 if (!ISSET(flags, GRND_NONBLOCK) && !ISSET(flags, GRND_INSECURE))
95 extractflags |= ENTROPY_WAIT|ENTROPY_SIG;
96 if (!ISSET(flags, GRND_INSECURE))
97 extractflags |= ENTROPY_HARDFAIL;
98 error = entropy_extract(seed, sizeof seed, extractflags);
99 if (error && !ISSET(flags, GRND_INSECURE))
100 goto out;
101
102 /* Instantiate the DRBG. */
103 if (nist_hash_drbg_instantiate(&drbg, seed, sizeof seed, NULL, 0,
104 NULL, 0))
105 panic("nist_hash_drbg_instantiate");
106
107 /* Promptly zero the seed. */
108 explicit_memset(seed, 0, sizeof seed);
109
110 /* Generate data. */
111 error = 0;
112 while (uio->uio_resid) {
113 size_t n = MIN(uio->uio_resid, RANDOM_BUFSIZE);
114
115 /*
116 * Clamp /dev/random output to the entropy capacity and
117 * seed size. Programs can't rely on long reads.
118 */
119 if (ISSET(flags, GRND_RANDOM)) {
120 n = MIN(n, ENTROPY_CAPACITY);
121 n = MIN(n, sizeof seed);
122 /*
123 * Guarantee never to return more than one
124 * buffer in this case to minimize bookkeeping.
125 */
126 CTASSERT(ENTROPY_CAPACITY <= RANDOM_BUFSIZE);
127 CTASSERT(sizeof seed <= RANDOM_BUFSIZE);
128 }
129
130 /*
131 * Try to generate a block of data, but if we've hit
132 * the DRBG reseed interval, reseed.
133 */
134 if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0)) {
135 /*
136 * Get a fresh seed without blocking -- we have
137 * already generated some output so it is not
138 * useful to block. This can fail only if the
139 * request is obscenely large, so it is OK for
140 * either /dev/random or /dev/urandom to fail:
141 * we make no promises about gigabyte-sized
142 * reads happening all at once.
143 */
144 error = entropy_extract(seed, sizeof seed,
145 ENTROPY_HARDFAIL);
146 if (error)
147 break;
148
149 /* Reseed and try again. */
150 if (nist_hash_drbg_reseed(&drbg, seed, sizeof seed,
151 NULL, 0))
152 panic("nist_hash_drbg_reseed");
153
154 /* Promptly zero the seed. */
155 explicit_memset(seed, 0, sizeof seed);
156
157 /* If it fails now, that's a bug. */
158 if (nist_hash_drbg_generate(&drbg, buf, n, NULL, 0))
159 panic("nist_hash_drbg_generate");
160 }
161
162 /* Transfer n bytes out. */
163 error = uiomove(buf, n, uio);
164 if (error)
165 break;
166
167 /*
168 * If this is /dev/random, stop here, return what we
169 * have, and force the next read to reseed. Programs
170 * can't rely on /dev/random for long reads.
171 */
172 if (ISSET(flags, GRND_RANDOM)) {
173 error = 0;
174 break;
175 }
176
177 /* Now's a good time to yield if needed. */
178 preempt_point();
179
180 /* Check for interruption after at least 256 bytes. */
181 CTASSERT(RANDOM_BUFSIZE >= 256);
182 if (__predict_false(curlwp->l_flag & LW_PENDSIG) &&
183 sigispending(curlwp, 0)) {
184 error = EINTR;
185 break;
186 }
187 }
188
189 out: /* Zero the buffer and free it. */
190 explicit_memset(buf, 0, RANDOM_BUFSIZE);
191 kmem_free(buf, RANDOM_BUFSIZE);
192
193 return error;
194 }
195
196 int
197 sys_getrandom(struct lwp *l, const struct sys_getrandom_args *uap,
198 register_t *retval)
199 {
200 /* {
201 syscallarg(void *) buf;
202 syscallarg(size_t) buflen;
203 syscallarg(unsigned) flags;
204 } */
205 void *buf = SCARG(uap, buf);
206 size_t buflen = SCARG(uap, buflen);
207 int flags = SCARG(uap, flags);
208 int error;
209
210 /* Set up an iov and uio to read into the user's buffer. */
211 struct iovec iov = { .iov_base = buf, .iov_len = buflen };
212 struct uio uio = {
213 .uio_iov = &iov,
214 .uio_iovcnt = 1,
215 .uio_offset = 0,
216 .uio_resid = buflen,
217 .uio_rw = UIO_READ,
218 .uio_vmspace = curproc->p_vmspace,
219 };
220
221 /* Validate the flags. */
222 if (flags & ~(GRND_RANDOM|GRND_INSECURE|GRND_NONBLOCK)) {
223 /* Unknown flags. */
224 error = EINVAL;
225 goto out;
226 }
227 if ((flags & (GRND_RANDOM|GRND_INSECURE)) ==
228 (GRND_RANDOM|GRND_INSECURE)) {
229 /* Nonsensical combination. */
230 error = EINVAL;
231 goto out;
232 }
233
234 /* Do it. */
235 error = dogetrandom(&uio, flags);
236
237 out: /*
238 * If we transferred anything, return the number of bytes
239 * transferred and suppress error; otherwise return the error.
240 */
241 *retval = buflen - uio.uio_resid;
242 if (*retval)
243 error = 0;
244 return error;
245 }
Cache object: 3534ccaf5a1f294f0c8fdc4ce177abad
|