blob: d79b86362c333373f7b060a45438fdaf6b6edcba [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
mcpowers8047c9f2006-05-01 16:17:15 -07005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
Krishna Yenduri9d31afc2009-02-27 12:20:10 -080021 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070022 * Use is subject to license terms.
Patrick Mooney80d56892017-09-22 23:43:19 +000023 *
24 * Copyright 2017 Joyent, Inc.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070025 */
26
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070027
28/*
29 * Random number generator pseudo-driver
30 *
31 * This is a lightweight driver which calls in to the Kernel Cryptographic
32 * Framework to do the real work. Kernel modules should NOT depend on this
33 * driver for /dev/random kernel API.
34 *
35 * Applications may ask for 2 types of random bits:
36 * . High quality random by reading from /dev/random. The output is extracted
37 * only when a minimum amount of entropy is available.
38 * . Pseudo-random, by reading from /dev/urandom, that can be generated any
39 * time.
40 */
41
42#include <sys/types.h>
43#include <sys/errno.h>
44#include <sys/stat.h>
45
46#include <sys/file.h>
47#include <sys/open.h>
48#include <sys/poll.h>
49#include <sys/uio.h>
50#include <sys/cred.h>
51#include <sys/modctl.h>
52#include <sys/conf.h>
53#include <sys/ddi.h>
54#include <sys/sunddi.h>
55#include <sys/random.h>
56#include <sys/crypto/impl.h>
57
58#define DEVRANDOM 0
59#define DEVURANDOM 1
60
61#define HASHSIZE 20 /* Assuming a SHA1 hash algorithm */
62#define WRITEBUFSIZE 512 /* Size of buffer for write request */
63#define MAXRETBYTES 1040 /* Max bytes returned per read. */
64 /* Must be a multiple of HASHSIZE */
65static dev_info_t *rnd_dip;
66
67static int rnd_open(dev_t *, int, int, cred_t *);
68static int rnd_close(dev_t, int, int, cred_t *);
69static int rnd_read(dev_t, struct uio *, cred_t *);
70static int rnd_write(dev_t, struct uio *, cred_t *);
71static int rnd_chpoll(dev_t, short, int, short *, struct pollhead **);
72static int rnd_attach(dev_info_t *, ddi_attach_cmd_t);
73static int rnd_detach(dev_info_t *, ddi_detach_cmd_t);
74static int rnd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
75
76/* DDI declarations */
77static struct cb_ops rnd_cb_ops = {
78 rnd_open, /* open */
79 rnd_close, /* close */
80 nodev, /* strategy */
81 nodev, /* print */
82 nodev, /* dump */
83 rnd_read, /* read */
84 rnd_write, /* write */
85 nodev, /* ioctl */
86 nodev, /* devmap */
87 nodev, /* mmap */
88 nodev, /* segmap */
89 rnd_chpoll, /* chpoll */
90 ddi_prop_op, /* prop_op */
91 NULL, /* streamtab */
92 (D_NEW | D_MP), /* cb_flag */
93 CB_REV, /* cb_rev */
94 nodev, /* aread */
95 nodev /* awrite */
96};
97
98static struct dev_ops rnd_ops = {
99 DEVO_REV, /* devo_rev, */
100 0, /* refcnt */
101 rnd_getinfo, /* get_dev_info */
102 nulldev, /* identify */
103 nulldev, /* probe */
104 rnd_attach, /* attach */
105 rnd_detach, /* detach */
106 nodev, /* reset */
107 &rnd_cb_ops, /* driver operations */
108 NULL, /* bus operations */
Sherry Moore19397402008-09-22 16:30:26 -0700109 NULL, /* power */
110 ddi_quiesce_not_needed, /* quiesce */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700111};
112
113/* Modlinkage */
114static struct modldrv modldrv = {
115 &mod_driverops,
Sherry Moore19397402008-09-22 16:30:26 -0700116 "random number device",
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700117 &rnd_ops
118};
119
120static struct modlinkage modlinkage = { MODREV_1, { &modldrv, NULL } };
121
122
123/* DDI glue */
124
125int
126_init(void)
127{
128 return (mod_install(&modlinkage));
129}
130
131int
132_fini(void)
133{
134 return (mod_remove(&modlinkage));
135}
136
137int
138_info(struct modinfo *modinfop)
139{
140 return (mod_info(&modlinkage, modinfop));
141}
142
143static int
144rnd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
145{
146 if (cmd != DDI_ATTACH)
147 return (DDI_FAILURE);
148
149 if (ddi_create_minor_node(dip, "random", S_IFCHR, DEVRANDOM,
150 DDI_PSEUDO, 0) == DDI_FAILURE) {
151 ddi_remove_minor_node(dip, NULL);
152 return (DDI_FAILURE);
153 }
154 if (ddi_create_minor_node(dip, "urandom", S_IFCHR, DEVURANDOM,
155 DDI_PSEUDO, 0) == DDI_FAILURE) {
156 ddi_remove_minor_node(dip, NULL);
157 return (DDI_FAILURE);
158 }
159
160 rnd_dip = dip;
161
162 return (DDI_SUCCESS);
163}
164
165static int
166rnd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
167{
168 if (cmd != DDI_DETACH)
169 return (DDI_FAILURE);
170
171 rnd_dip = NULL;
172 ddi_remove_minor_node(dip, NULL);
173
174 return (DDI_SUCCESS);
175}
176
177/*ARGSUSED*/
178static int
179rnd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
180{
181 int error;
182
183 switch (infocmd) {
184 case DDI_INFO_DEVT2DEVINFO:
185 *result = rnd_dip;
186 error = DDI_SUCCESS;
187 break;
188 case DDI_INFO_DEVT2INSTANCE:
189 *result = (void *)0;
190 error = DDI_SUCCESS;
191 break;
192 default:
193 error = DDI_FAILURE;
194 }
195 return (error);
196}
197
198/*ARGSUSED3*/
199static int
200rnd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
201{
202 switch (getminor(*devp)) {
203 case DEVRANDOM:
204 if (!kcf_rngprov_check())
205 return (ENXIO);
206 break;
207 case DEVURANDOM:
208 break;
209 default:
210 return (ENXIO);
211 }
212 if (otyp != OTYP_CHR)
213 return (EINVAL);
214
215 if (flag & FEXCL)
216 return (EINVAL);
217 return (0);
218}
219
220/*ARGSUSED*/
221static int
222rnd_close(dev_t dev, int flag, int otyp, cred_t *credp)
223{
224 return (0);
225}
226
227/*ARGSUSED2*/
228static int
229rnd_read(dev_t dev, struct uio *uiop, cred_t *credp)
230{
231 size_t len;
232 minor_t devno;
233 int error = 0;
234 int nbytes = 0;
235 uint8_t random_bytes[2 * HASHSIZE];
236
237 devno = getminor(dev);
238
239 while (error == 0 && uiop->uio_resid > 0) {
240 len = min(sizeof (random_bytes), uiop->uio_resid);
241 switch (devno) {
242 case DEVRANDOM:
243 error = kcf_rnd_get_bytes(random_bytes, len,
Krishna Yenduri8b502712009-05-12 17:23:17 -0700244 uiop->uio_fmode & (FNDELAY|FNONBLOCK));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700245 break;
246 case DEVURANDOM:
247 error = kcf_rnd_get_pseudo_bytes(random_bytes, len);
248 break;
249 default:
250 return (ENXIO);
251 }
252
253 if (error == 0) {
254 /*
255 * /dev/[u]random is not a seekable device. To prevent
256 * uio offset from growing and eventually exceeding
257 * the maximum, reset the offset here for every call.
258 */
259 uiop->uio_loffset = 0;
260 error = uiomove(random_bytes, len, UIO_READ, uiop);
261
262 nbytes += len;
263
krishnaf317a3a2006-11-11 21:39:34 -0800264 if (devno == DEVRANDOM && nbytes >= MAXRETBYTES)
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700265 break;
266
267 } else if ((error == EAGAIN) && (nbytes > 0)) {
268 error = 0;
269 break;
270 }
271 }
272 return (error);
273}
274
275/*ARGSUSED*/
276static int
277rnd_write(dev_t dev, struct uio *uiop, cred_t *credp)
278{
279 int error;
280 uint8_t buf[WRITEBUFSIZE];
281 size_t bytes;
mcpowers8047c9f2006-05-01 16:17:15 -0700282 minor_t devno;
283
284 devno = getminor(dev);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700285
286 while (uiop->uio_resid > 0) {
287 bytes = min(sizeof (buf), uiop->uio_resid);
288
289 /* See comments in rnd_read() */
290 uiop->uio_loffset = 0;
291 if ((error = uiomove(buf, bytes, UIO_WRITE, uiop)) != 0)
292 return (error);
293
mcpowers8047c9f2006-05-01 16:17:15 -0700294 switch (devno) {
295 case DEVRANDOM:
296 if ((error = random_add_entropy(buf, bytes, 0)) != 0)
297 return (error);
298 break;
299 case DEVURANDOM:
300 if ((error = random_add_pseudo_entropy(buf, bytes,
301 0)) != 0)
302 return (error);
303 break;
304 default:
305 return (ENXIO);
306 }
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700307 }
308
309 return (0);
310}
311
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800312static struct pollhead urnd_pollhd;
313
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700314/*
315 * poll(2) is supported as follows:
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800316 * . Only POLLIN, POLLOUT, and POLLRDNORM events are supported.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700317 * . POLLOUT always succeeds.
318 * . POLLIN and POLLRDNORM from /dev/urandom always succeeds.
319 * . POLLIN and POLLRDNORM from /dev/random will block until a
320 * minimum amount of entropy is available.
321 */
322static int
323rnd_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800324 struct pollhead **phpp)
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700325{
326 switch (getminor(dev)) {
327 case DEVURANDOM:
328 *reventsp = events & (POLLOUT | POLLIN | POLLRDNORM);
329
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800330 /*
331 * A non NULL pollhead pointer should be returned in case
332 * user polls for 0 events.
333 */
Patrick Mooney80d56892017-09-22 23:43:19 +0000334 if ((*reventsp == 0 && !anyyet) || (events & POLLET))
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800335 *phpp = &urnd_pollhd;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700336
337 break;
338 case DEVRANDOM:
Krishna Yenduri9d31afc2009-02-27 12:20:10 -0800339 kcf_rnd_chpoll(events, anyyet, reventsp, phpp);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700340 break;
341 default:
342 return (ENXIO);
343 }
344
345 return (0);
346}